Objective-C가 개인 메서드를 지원하지 않는 이유는 무엇입니까?


123

Objective-C 에서 semi-private 메서드를 선언하는 여러 전략을 보았지만 진정한 private 메서드를 만드는 방법은없는 것 같습니다. 동의합니다. 그러나 이것이 왜 그렇습니까? 내가 본질적으로 말한 모든 설명은 "당신은 할 수 없지만 여기에 가까운 근사치입니다."

ivars범위를 제어하는 ​​(멤버)에 적용되는 여러 키워드가 있습니다 (예 : @private,, @public) @protected. 방법에 대해서도이 작업을 수행 할 수없는 이유는 무엇입니까? 런타임이 지원할 수 있어야하는 것 같습니다. 내가 놓친 근본적인 철학이 있습니까? 의도적입니까?


15
좋은 질문입니다, btw.
bbum

5
당신의 편집에 : 예, 런타임 시행은 매우 적은 이익을 위해 매우 비쌉니다. 컴파일 시간 적용은 언어에 대한 환영할만한 추가 사항이며 매우 달성 가능합니다. 예, 런타임에 우회 할 수 있지만 괜찮습니다. 프로그래머는 적이 아닙니다. 범위의 목표는 프로그래머를 오류로부터 보호하는 것입니다.
Rob Napier

1
"런타임을 건너 뛸"수 없습니다. 런타임은 메시지 전달을 수행합니다.
Chuck

"개인적인 방법 으로이 수표 를 단락시킬 방법이 없을 것입니다 " "우회"를 의미 했습니까? ;-)
Constantino Tsarouhas 2012 년

답변:


103

대답은 ... 음 ... 간단합니다. 사실 단순성과 일관성.

Objective-C는 메소드 디스패치 시점에서 순전히 동적입니다. 특히 모든 메서드 디스패치는 다른 모든 메서드 디스패치와 똑같은 동적 메서드 확인 지점을 거칩니다. 런타임시 모든 메서드 구현은 정확히 동일한 노출을 가지며 메서드 및 선택기와 함께 작동하는 Objective-C 런타임에서 제공하는 모든 API는 모든 메서드에서 동일하게 작동합니다.

많은 사람들이 대답했듯이 (여기와 다른 질문 모두에서), 컴파일 타임 프라이빗 메서드가 지원됩니다. 클래스가 공개적으로 사용 가능한 인터페이스에서 메서드를 선언하지 않으면 해당 메서드는 코드에 관한 한 존재하지 않을 수도 있습니다. 즉, 프로젝트를 적절하게 구성하여 컴파일 시간에 원하는 다양한 가시성 조합을 모두 얻을 수 있습니다.

동일한 기능을 런타임에 복제해도 이점이 거의 없습니다. 그것은 엄청난 양의 복잡성과 오버 헤드를 추가 할 것입니다. 그리고 그 모든 복잡성에도 불구하고 가장 평범한 개발자가 "개인"메서드를 실행하는 것을 막지는 못합니다.

편집 : 내가 알아 차린 가정 중 하나는 개인 메시지가 런타임을 거쳐야하므로 잠재적으로 큰 오버 헤드가 발생할 수 있다는 것입니다. 이것이 절대적으로 사실입니까?

네, 그렇습니다. 클래스의 구현자가 구현의 모든 Objective-C 기능 세트를 사용하지 않을 것이라고 가정 할 이유가 없으며 이는 동적 디스패치가 발생해야 함을 의미합니다. 그러나objc_msgSend() 컴파일러가 전용 메서드임을 알 수 있기 때문에 전용 메서드를의 특수 변형에 의해 디스패치 할 수없는 특별한 이유가 없습니다 . 즉, 이것은 Class구조에 개인 전용 메소드 테이블을 추가하여 달성 할 수 있습니다 .

private 메서드가이 검사를 단락 시키거나 런타임을 건너 뛸 수있는 방법이 없습니까?

런타임을 건너 뛸 수는 없지만 런타임 반드시 private 메서드를 검사 할 필요는 없습니다 .

즉, 타사가 objc_msgSendPrivate()해당 객체의 구현 외부에서 의도적으로 객체를 호출 할 수 없으며 일부 작업 (예 : KVO)이이를 수행해야 할 이유가 없습니다. 사실상, 그것은 단지 관례 일 뿐이며, 개인 메소드의 선택자를 접두사로 붙이거나 인터페이스 헤더에 언급하지 않는 것보다 실제로는 조금 더 낫습니다.

하지만 그렇게하는 것은 언어의 순수한 동적 특성을 훼손 할 것입니다. 더 이상 모든 메소드 디스패치가 동일한 디스패치 메커니즘을 거치지 않습니다. 대신 대부분의 방법이 한 방향으로 작동하고 소수는 다른 상황에 처하게됩니다.

이것은 Objective-C의 일관된 역 동성 위에 구축 된 Cocoa의 많은 메커니즘이 있기 때문에 런타임을 넘어 확장됩니다. 예를 들어, Key Value Coding과 Key Value Observation은 모두 개인 메서드를 지원하기 위해 매우 많이 수정해야합니다 (대부분 악용 가능한 허점을 생성하여). 그렇지 않으면 개인 메서드가 호환되지 않습니다.


49
+1 Objective-C는 (어떤면에서) 프로그래머가 매 턴마다 컴파일러 / 런타임에 의해 손에 찔리지 않고 일정량의 규율을 보일 것으로 예상되는 "빅보이"언어입니다. 나는 그것이 상쾌함을 느낀다. 나에게는 컴파일 / 링크 중에 메서드 가시성을 제한하는 것이 충분하고 바람직합니다.
Quinn Taylor

11
더 많은 파이썬은 "obj-c-ic"입니다 :). Guido는 PyObjC의 첫 번째 버전을 만드는 것을 포함하여 NeXT 시스템에서 Python을 유지 관리하는 데 매우 적극적이었습니다. 따라서 ObjC는 파이썬에 다소 영향을 미쳤습니다 .
bbum

3
삶에 대한 자비로운 독재자와 신화적인 NeXT 시스템과의 교차점에 대한 흥미로운 역사적 이야기에 +1.
pokstad

5
감사합니다. Python, Java 및 Objective-C는 모두 [SmallTalk와 유사한] 런타임 개체 모델을 가지고 있습니다. Java는 Objective-C에서 직접 파생되었습니다. Python은 영감을주는 것 이상의 병렬 과정을 실행했지만 Guido는 확실히 NeXTSTEP을 면밀히 연구했습니다.
bbum 2010-01-29

1
라이센스를 신뢰하지 않고 gcc보다 clang을 확실히 선호하는 것을 제외하고는. 그러나 그것은 확실히 좋은 첫 번째 단계입니다.
Cthutu 2011

18

런타임은이를 지원할 수 있지만 비용은 엄청납니다. 전송되는 모든 선택기는 해당 클래스에 대해 비공개인지 공개인지 확인해야합니다. 또는 각 클래스는 두 개의 개별 디스패치 테이블을 관리해야합니다. 이 보호 수준은 컴파일 타임에 수행되기 때문에 인스턴스 변수의 경우 동일하지 않습니다.

또한 런타임은 개인 메시지의 발신자가 수신자와 동일한 클래스인지 확인해야합니다. 개인 메서드를 우회 할 수도 있습니다. 만약 클래스가 사용 instanceMethodForSelector:된다면, 그것은 IMP그들이 private 메서드를 직접 호출 할 수 있도록 다른 클래스에 반환 된 을 줄 수 있습니다.

개인 메서드는 메시지 디스패치를 ​​우회 할 수 없습니다. 다음 시나리오를 고려하십시오.

  1. 클래스 AllPublic에는 공용 인스턴스 메서드가 있습니다.doSomething

  2. 다른 클래스 HasPrivate에는doSomething

  3. 당신은 모두의 인스턴스의 수를 포함하는 배열을 생성 AllPublic하고HasPrivate

  4. 다음 루프가 있습니다.

    for (id anObject in myArray)
        [anObject doSomething];

    내부에서 해당 루프를 실행 AllPublic하면 런타임 doSomething에서 HasPrivate인스턴스 전송을 중지해야 하지만이 루프는 HasPrivate클래스 내부에 있으면 사용할 수 있습니다 .


1
비용이 발생한다는 데 동의합니다. 아마도 휴대용 장치에서 원하는 것이 아닐 수도 있습니다. 그렇지 않으면 엄청난 한정자를 지원할 수 있습니까? 비용이 데스크톱 시스템에서 정말로 중요할까요?
Rob Jones

1
하지만 당신이 옳을 수 있습니다. Objective-C는 런타임 메시지 디스패치와 C ++의 컴파일 타임 메서드 호출이 있으므로 컴파일 타임 검사와 런타임 검사에 대해 이야기 할 것입니다.
Remus Rusanu

private 메서드를 지원하지 않는 주된 이유는 성능 때문이라는 것이 정말 이상해 보입니다. 나는 애플이 성능 저하가 있든 없든 사적인 방법이 매우 필요하다고 생각하지 않았다고 생각한다. 그렇지 않으면 수십억 달러 규모의 회사를 위해 다른 언어를 선택해야했습니다.
pokstad

1
private 메서드를 추가하면 런타임 구현이 복잡해질뿐만 아니라 언어의 의미도 복잡해집니다. 액세스 제어는 정적 언어에서 단순한 개념이지만, 많은 개념적 오버 헤드를 수반하고 많은 "이것이 작동하지 않는 이유"로 이어지는 동적 디스패치에는 적합하지 않습니다. 질문.
Jens Ayton

7
사적인 방법이 실제로 무엇을 사겠습니까? Objective-C는 결국 C 기반 언어입니다. 따라서 누군가 프로세스에서 실행중인 코드가있는 경우 API가 얼마나 비공개 또는 난독 화되었는지에 관계없이 프로세스를 쉽게 p0wnz 할 수 있습니다.
bbum

13

지금까지 게시 된 답변은 철학적 관점에서 질문에 대한 답변을 잘 수행하므로보다 실용적인 이유를 가정하겠습니다. 언어의 의미를 변경하면 무엇을 얻을 수 있습니까? 개인 메서드를 효과적으로 "숨기기"만큼 간단합니다. 예를 들어, 다음과 같이 헤더 파일에 선언 된 클래스가 있다고 상상해보십시오.

@interface MyObject : NSObject {}
- (void) doSomething;
@end

"비공개"메소드가 필요한 경우이를 구현 파일에 넣을 수도 있습니다.

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

물론, C ++ / Java private 메서드와 완전히 같지는 않지만 효과적으로 충분히 가까우므로 컴파일러, 런타임 등 언어의 의미를 변경하여 이미 허용되는 기능을 추가하는 이유는 무엇입니까? 방법? 다른 답변에서 언급했듯이 메시지 전달 의미 체계와 런타임 리플렉션에 대한 의존성으로 인해 "개인"메시지 처리가 사소하지 않게됩니다.


1
이 접근 방식의 문제점은 누군가가 클래스를 서브 클래 싱 할 때 (알지 못하더라도) private 메서드를 재정의 할 수 있다는 것입니다. 기본적으로 재정의 될 가능성이없는 이름을 선택해야한다고 생각합니다.
dreamlax

3
나에게 해킹 위에 해킹처럼 느껴집니다.
Rob Jones

1
@Mike : Apple은 선행 밑줄이있는 메서드 이름이 명시 적으로 Apple 전용으로 예약되어 있다고 밝혔습니다. developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . 권장되는 대안은 두 개의 대문자와 함께 접두사입니다 다음 밑줄.
dreamlax

8
또한 다른 프로그래머가 누가 작성했는지 알 수 있도록 방법 뒤에 SSN을 넣으십시오. = D 네임 스페이스, 필요합니다 !!!
pokstad

2
당신은 당신이 오버라이드 (override)되는 정의하는 방법에 대해 걱정하는 경우, 당신은 제대로 오브젝티브 C가 장려 상세 고유의 수준을 수용하지 않은 ...
켄달 Helmstetter Gelner

7

가장 쉬운 해결책은 Objective-C 클래스에서 일부 정적 C 함수를 선언하는 것입니다. 이것들은 static 키워드에 대한 C 규칙에 따라 파일 범위 만 가지며, 그 때문에 해당 클래스의 메서드에서만 사용할 수 있습니다.

전혀 소란이 없습니다.


@implementation 섹션 외부에서 정의해야합니까?
Rob Jones

@Rob-클래스 구현 내부에 정의 할 수 없습니다.
Cromulent 2010

6

예, C ++를 처리하기 위해 컴파일러에서 이미 사용하는 기술인 이름 변경 (name-mangling)을 활용하면 런타임에 영향을주지 않고 수행 할 수 있습니다.

다른 기술 (예 : 접두사 또는 밑줄)이 충분히 우회 할 수있는 코딩 문제 공간의 상당한 어려움을 해결할 수 있다는 것이 확립되지 않았기 때문에 수행되지 않았습니다. IOW, 내재 된 습관을 극복하려면 더 많은 고통이 필요합니다.

clang 또는 gcc에 패치를 제공하여 구문에 개인 메서드를 추가하고 컴파일 중에 단독으로 인식하고 즉시 잊어 버린 이름을 생성 할 수 있습니다. 그러면 Objective-C 커뮤니티의 다른 사람들이 실제로 가치가 있는지 여부를 결정할 수 있습니다. 개발자를 설득하는 것보다 그 방법이 더 빠를 것입니다.


2
컴파일 시간 이름 맹 글링은 XIB 파일에 나타날 수있는 메소드 선택자와 NSSelectorFromString (), KeyValueCoding & friends와 같은 것들에 전달 될 수있는 것들을 포함하여 모든 메소드 선택기를 망가뜨려 야하기 때문에 생각하는 것보다 훨씬 어렵습니다 ...
bbum

1
예, 도구 체인 전체에서 비공개 메서드에 대한 지원을 제공하려는 경우 더 복잡 할 것입니다. 컴파일러의 기호 테이블을 저장하고 일부 도구 모음 API를 통해 액세스 할 수 있어야합니다. Apple이 시간과 안정성이 허용되는 한 충분히 중요한 기능을 개발자에게 점진적으로 배포 한 것은 이번이 처음이 아닙니다. 그러나 사적인 방법과 관련된 경우 : 노력을 수행하기에 충분한 승리인지 여부는 의문입니다. 이러한 다른 메커니즘에 의해 클래스 외부에서 액세스 할 수 있어야한다는 것은 더욱 의심 스럽습니다.
Huperniketes

1
컴파일 타임 전용 시행 + 이름 맹 글링이 클래스의 메서드 목록을 걷다가 거기에서 찾는 것을 어떻게 막을까요?
dreamlax 2010 년

그렇지 않습니다. 내 대답은 OP가 제기 한 질문에만 해당됩니다.
Huperniketes

나는 Clang에 private 메서드를 추가하려고 시도하는 것에 대해 생각했습니다. private 메서드가 좋을 것이라고 생각한 한 가지 이유는 단순한 c 함수처럼 직접 호출 할 수 있고 모든 일반적인 C 함수 최적화가 발생할 수 있다는 것입니다. 나는 시간 때문에 귀찮게 한 적이 없으며 이미 c를 사용하여 이것을 할 수 있습니다.
Nathan Day

4

본질적으로 Objective-C의 메시지 전달 형식의 메서드 호출과 관련이 있습니다. 모든 메시지는 모든 개체에 보낼 수 있으며 개체는 메시지에 응답하는 방법을 선택합니다. 일반적으로 메시지의 이름을 따서 명명 된 메서드를 실행하여 응답하지만 다른 여러 방법으로도 응답 할 수 있습니다. 이것은 private 메서드를 완전히 불가능하게 만들지는 않습니다. Ruby는 비슷한 메시지 전달 시스템을 사용합니다. 그러나 다소 어색하게 만듭니다.

Ruby의 private 메서드 구현조차도 이상하기 때문에 사람들에게 약간 혼란 스럽습니다 ( 이 목록에 있는 메시지를 제외하고 원하는 메시지를 객체에 보낼 수 있습니다 !). 기본적으로 Ruby는 명시 적 수신자를 사용하여 비공개 메서드를 호출하는 것을 금지하여 작동합니다. Objective-C에서는 Objective-C에 해당 옵션이 없기 때문에 더 많은 작업이 필요합니다.


1

Objective-C의 런타임 환경 문제입니다. C / C ++ 가 읽을 수없는 기계어 코드로 컴파일 되는 동안 Objective-C는 여전히 메서드 이름과 같은 일부 사람이 읽을 수있는 속성을 문자열로 유지 합니다. 이를 통해 Objective-C는 반사 기능 을 수행 할 수 있습니다.

편집 : 엄격한 개인 메서드가없는 반사 언어는 Objective-C가 호출 할 수있는 메서드를 제한하는 대신 코드를 사용하는 다른 사람들을 신뢰한다는 점에서 더 "파이썬"합니다. 이중 밑줄과 같은 명명 규칙을 사용하는 것은 일반 클라이언트 코더로부터 코드를 숨기는 것을 의미하지만 더 심각한 작업을 수행해야하는 코더를 중지하지는 않습니다.


그리고 lib의 소비자가 개인 메서드를 반영 할 수 있다면 그들도 호출 할 수 없습니까?
Jared Updike

그들이 어디를 봐야할지 안다면 사람들이 아이폰에서 문서화되지 않은 라이브러리를 사용하고 있지 않나요? iPhone의 문제는 앱이 비공개 API를 사용하도록 허용되지 않을 위험이 있다는 것입니다.
pokstad

그들이 성찰을 통해 사적인 방법에 접근한다면, 그들은 자격이있는 것을 얻습니다. 모든 오류는 귀하의 잘못이 아닙니다.
gnasher729

1

질문의 해석에 따라 두 가지 답변이 있습니다.

첫 번째는 인터페이스에서 메서드 구현을 숨기는 것입니다. 일반적으로 이름이없는 범주 (예 :)와 함께 사용됩니다 @interface Foo(). 이렇게하면 객체가 이러한 메시지를 보낼 수 있지만 다른 메시지는 보낼 수 없습니다.하지만 우연히 (또는 다른 방법으로) 덮어 쓸 수 있습니다.

두 번째 대답은 이것이 성능과 인라인에 관한 것이라는 가정하에 가능하지만 대신 로컬 C 함수로 가능합니다. 'private foo ( NSString *arg)'메소드 를 원하면 . void MyClass_foo(MyClass *self, NSString *arg)과 같은 C 함수로 호출합니다 MyClass_foo(self,arg). 구문은 다르지만 C ++의 private 메서드의 정상적인 성능 특성으로 작동합니다.

이것이 질문에 대한 답이지만, 이름없는 카테고리가 훨씬 더 일반적인 Objective-C 방식이라는 점을 지적해야합니다.


0

Objective-C는 개인 메서드가 필요하지 않기 때문에 지원하지 않습니다.

C ++에서 모든 메서드는 클래스 선언에 표시되어야합니다. 헤더 파일을 포함한 누군가가 볼 수없는 메소드를 가질 수 없습니다. 따라서 구현 외부의 코드에서 사용해서는 안되는 메서드를 원한다면 선택의 여지가 없습니다. 컴파일러는 메서드를 사용해서는 안된다고 말할 수있는 도구를 제공해야합니다. 즉, "private"키워드입니다.

Objective-C에서는 헤더 파일에없는 메소드를 가질 수 있습니다. 따라서 헤더 파일에 메소드를 추가하지 않음으로써 동일한 목적을 매우 쉽게 달성 할 수 있습니다. 개인 방법이 필요하지 않습니다. Objective-C는 또한 private 메서드를 변경했기 때문에 클래스의 모든 사용자를 다시 컴파일 할 필요가 없다는 장점이 있습니다.

예를 들어 헤더 파일 (더 이상 아님)에서 선언해야했던 변수, @private, @public 및 @protected를 사용할 수 있습니다.


0

여기서 누락 된 대답은 다음과 같습니다. 개인 방법은 진화 가능성의 관점에서 볼 때 나쁜 생각이기 때문입니다. 메소드를 작성할 때 비공개로 만드는 것이 좋은 생각처럼 보일 수 있지만 이는 초기 바인딩의 한 형태입니다. 컨텍스트가 변경 될 수 있으며 나중에 사용자가 다른 구현을 사용하려고 할 수 있습니다. 약간 도발적입니다. "애자일 개발자는 비공개 방법을 사용하지 않습니다."

스몰 토크와 마찬가지로 Objective-C는 어른 프로그래머를위한 것입니다. 우리는 원래 개발자가 인터페이스가 무엇이라고 가정했는지 아는 것을 중요하게 생각하며 구현을 변경해야 할 경우 결과를 처리 할 책임을집니다. 네, 그것은 구현이 아니라 철학입니다.


나는 이것을 찬성 투표 할 자격이 없기 때문에 찬성했습니다. Stephan이 말했듯이 "개인"메서드가 필요하지 않다는 주장이 있으며, 선호하는 결론이 무엇이든 양측 모두 요점이 있다는 점에서 동적 대 정적 형식에 대한 주장과 유사합니다.
alastair
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.