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

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

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



답변

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

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

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

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

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

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

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

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

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

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

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


답변

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

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

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

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

  2. 다른 클래스 HasPrivate에는doSomething

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

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

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

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


답변

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

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


답변

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

전혀 소란이 없습니다.


답변

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

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

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


답변

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

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


답변

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

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