개발자가 + initialize 또는 + load를 재정의하는 상황을 이해하는 데 관심이 있습니다. 문서에 따르면 Objective-C 런타임에서 이러한 메서드가 호출된다는 사실이 분명해졌지만,이 메서드에 대한 문서에서 분명한 것은 이것이 전부입니다. 🙂
저의 호기심은 Apple의 예제 코드 인 MVCNetworking을 살펴 보는 것입니다. 그들의 모델 클래스에는 +(void) applicationStartup
메서드가 있습니다. 파일 시스템에서 몇 가지 하우스 키핑을 수행하고 NSDefaults 등을 읽습니다. 그리고 NSObject의 클래스 메소드를 검색 한 후이 청소 작업을 + load에 넣어도 괜찮을 것 같습니다.
나는 MVCNetworking 프로젝트를 수정하고, App Delegate에서 + applicationStartup에 대한 호출을 제거하고, + load에 하우스 키핑 비트를 넣었습니다. 내 컴퓨터는 불이 붙지 않았지만 그것이 옳다는 의미는 아닙니다! + load 또는 + initialize와 비교하여 호출해야하는 사용자 지정 설정 방법에 대한 미묘함, 문제 및 기타 사항에 대한 이해를 얻고 싶습니다.
+ load 문서에 대해 다음과 같이 말합니다.
로드 메시지는 동적으로로드되고 정적으로 링크 된 클래스 및 카테고리로 전송되지만 새로로드 된 클래스 또는 카테고리가 응답 할 수있는 메소드를 구현하는 경우에만 전송됩니다.
이 문장은 모든 단어의 정확한 의미를 모르면 kludgey하고 구문 분석하기 어렵습니다. 도움!
-
“동적로드 및 정적으로 링크 됨”이란 무엇을 의미합니까? 무언가를 동적으로로드하고 정적으로 연결할 수 있습니까? 아니면 상호 배타적입니까?
-
“… 새로로드 된 클래스 또는 범주가 응답 할 수있는 메서드를 구현합니다.”어떤 메서드? 어떻게 반응합니까?
+ initialize에 관해서는 문서에 다음과 같이 나와 있습니다.
초기화는 클래스 당 한 번만 호출됩니다. 클래스 및 클래스 범주에 대해 독립적 인 초기화를 수행하려면로드 메서드를 구현해야합니다.
“클래스를 설정하려는 경우 … 초기화를 사용하지 마십시오.”라는 의미입니다. 좋아. 언제 또는 왜 초기화를 재정의합니까?
답변
load
메시지
런타임은 load
클래스 개체가 프로세스의 주소 공간에로드 된 직후에 각 클래스 개체에 메시지를 보냅니다 . 프로그램 실행 파일의 일부인 클래스의 경우 런타임은 load
프로세스 수명 초기에 메시지를 보냅니다 . 공유 (동적으로로드 된) 라이브러리에있는 클래스의 경우 런타임은 공유 라이브러리가 프로세스의 주소 공간에로드 된 직후로드 메시지를 보냅니다.
또한 런타임은 load
해당 클래스 객체 자체가 load
메서드를 구현하는 경우 에만 클래스 객체로 전송 합니다 . 예:
@interface Superclass : NSObject
@end
@interface Subclass : Superclass
@end
@implementation Superclass
+ (void)load {
NSLog(@"in Superclass load");
}
@end
@implementation Subclass
// ... load not implemented in this class
@end
런타임은 load
메시지를 Superclass
클래스 객체 로 보냅니다 . 그것은 않습니다 하지 보내고 load
받는 메시지 Subclass
에도 불구하고, 클래스 객체 Subclass
상속의 방법 Superclass
.
런타임은 클래스의 모든 수퍼 클래스 객체 (이 수퍼 클래스 객체가 구현하는 경우 )와 사용자가 링크 한 공유 라이브러리의 모든 클래스 객체에 메시지를 load
보낸 후 클래스 객체에 메시지를 보냅니다 . 그러나 자신의 실행 파일에서 어떤 다른 클래스가 아직 수신되었는지 알 수 없습니다 .load
load
load
프로세스가 주소 공간에로드하는 모든 클래스는 프로세스가 클래스를 다른 용도로 사용하는지 여부에 관계없이 메소드를 load
구현하면 메시지 를 수신합니다 load
.
당신은 런타임이 조회하는 방법을 볼 수 있습니다 load
의 특별한 경우로 방법 _class_getLoadMethod
의 objc-runtime-new.mm
, 그리고에서 직접 호출 call_class_loads
에 objc-loadmethod.mm
.
런타임 load
은 동일한 클래스의 여러 카테고리가 .NET Framework를 구현하더라도로드하는 모든 카테고리 의 메소드를 실행합니다 load
. 이것은 드문 일입니다. 일반적으로 두 범주가 동일한 클래스에서 동일한 메서드를 정의하면 메서드 중 하나가 “승리”하여 사용되고 다른 메서드는 호출되지 않습니다.
initialize
방법
런타임 은 클래스 객체 또는 클래스의 인스턴스에 initialize
첫 번째 메시지 ( load
또는 initialize
제외)를 보내기 직전에 클래스 객체 의 메서드를 호출합니다 . 이 메시지는 일반 메커니즘을 사용하여 전송되므로 클래스 initialize
가을 구현하지 않지만 구현 하는 클래스에서 상속하면 클래스가 수퍼 클래스의 initialize
. 런타임은 initialize
먼저 클래스의 모든 슈퍼 클래스에를 보냅니다 (슈퍼 클래스가 아직 전송되지 않은 경우 initialize
).
예:
@interface Superclass : NSObject
@end
@interface Subclass : Superclass
@end
@implementation Superclass
+ (void)initialize {
NSLog(@"in Superclass initialize; self = %@", self);
}
@end
@implementation Subclass
// ... initialize not implemented in this class
@end
int main(int argc, char *argv[]) {
@autoreleasepool {
Subclass *object = [[Subclass alloc] init];
}
return 0;
}
이 프로그램은 두 줄의 출력을 인쇄합니다.
2012-11-10 16:18:38.984 testApp[7498:c07] in Superclass initialize; self = Superclass
2012-11-10 16:18:38.987 testApp[7498:c07] in Superclass initialize; self = Subclass
시스템이 initialize
메소드를 느리게 전송하기 때문에 프로그램이 실제로 메시지를 클래스 (또는 하위 클래스 또는 클래스 또는 하위 클래스의 인스턴스)로 보내지 않는 한 클래스는 메시지를받지 않습니다. 그리고 당신이를받을 때까지 initialize
, 당신의 과정에있는 모든 수업은 이미 load
(적절하다면) 받았을 것 입니다.
구현하는 표준 방법 initialize
은 다음과 같습니다.
@implementation Someclass
+ (void)initialize {
if (self == [Someclass class]) {
// do whatever
}
}
이 패턴의 요점은 Someclass
구현하지 않는 하위 클래스가있을 때 자체적으로 다시 초기화 되는 것을 방지 하는 것 initialize
입니다.
런타임은의 함수에 initialize
메시지를 보냅니다 . 보내는 데 사용 하는 것을 알 수 있는데 , 이는 일반적인 메시지 전송 기능입니다. _class_initialize
objc-initialize.mm
objc_msgSend
추가 읽기
이 주제에 대한 Mike Ash의 금요일 Q & A 를 확인하십시오 .
답변
그것이 의미하는 바는 +initialize
카테고리에서 재정의하지 않는다는 것입니다. 아마도 무언가를 깨뜨릴 것입니다.
+load
클래스 또는 구현하는 카테고리에 한 번이라고 +load
, 가능한 한 빨리 그 클래스 또는 카테고리가로드됩니다. “정적으로 연결됨”이라는 것은 앱 바이너리로 컴파일되었음을 의미합니다. +load
이렇게 컴파일 된 클래스 의 메서드는 앱이 시작될 때, 아마도 main()
. “동적으로로드 됨”이라고 표시되면 플러그인 번들 또는에 대한 호출을 통해로드됨을 의미합니다 dlopen()
. iOS를 사용하는 경우이 경우를 무시할 수 있습니다.
+initialize
메시지를 처리하기 직전에 클래스에 메시지가 처음 전송 될 때 호출됩니다. 이것은 (분명히) 한 번만 발생합니다. +initialize
카테고리에서 재정의하면 다음 세 가지 중 하나가 발생합니다.
- 카테고리 구현이 호출되고 클래스의 구현이
- 다른 사람의 카테고리 구현이 호출됩니다. 당신이 쓴 것은 아무것도하지 않습니다
- 카테고리가 아직로드되지 않았고 해당 구현이 호출되지 않습니다.
이것이 +initialize
카테고리에서 재정의해서는 안되는 이유 입니다. 사실 어떤 항목을 대체하는지 또는 자신의 대체 항목이 다른 카테고리에 의해 자체적으로 전환 될지 확실하지 않기 때문에 카테고리의 메소드를 바꾸는 것은 매우 위험합니다 .
BTW, 고려해야 할 또 다른 문제 +initialize
는 누군가가 당신을 하위 클래스로 만들면 잠재적으로 클래스에 대해 한 번, 각 하위 클래스에 대해 한 번씩 호출 될 수 있다는 것입니다. static
변수 설정과 같은 작업을 수행하는 경우을 사용 dispatch_once()
하거나 테스트 하여이를 방지하고 싶을 것 self == [MyClass class]
입니다.