NSObject + load 및 + initialize-그들은 무엇을합니까?


115

개발자가 + initialize 또는 + load를 재정의하는 상황을 이해하는 데 관심이 있습니다. 문서에 따르면 Objective-C 런타임에서 이러한 메서드가 호출된다는 사실이 분명해졌지만,이 메서드에 대한 문서에서 분명한 것은 이것이 전부입니다. :-)

저의 호기심은 Apple의 예제 코드 인 MVCNetworking을 살펴 보는 것입니다. 그들의 모델 클래스에는 +(void) applicationStartup메서드가 있습니다. 파일 시스템에서 몇 가지 하우스 키핑을 수행하고 NSDefaults 등을 읽습니다. 그리고 NSObject의 클래스 메소드를 검색 한 후이 청소 작업을 + load에 넣어도 괜찮을 것 같습니다.

나는 MVCNetworking 프로젝트를 수정하고, App Delegate에서 + applicationStartup에 대한 호출을 제거하고, + load에 하우스 키핑 비트를 넣었습니다. 내 컴퓨터는 불이 붙지 않았지만 그것이 옳다는 의미는 아닙니다! + load 또는 + initialize와 비교하여 호출해야하는 사용자 지정 설정 방법에 대한 미묘함, 문제 및 기타 사항에 대한 이해를 얻고 싶습니다.


+ load 문서에 대해 다음과 같이 말합니다.

로드 메시지는 동적으로로드되고 정적으로 링크 된 클래스 및 카테고리로 전송되지만 새로로드 된 클래스 또는 카테고리가 응답 할 수있는 메소드를 구현하는 경우에만 전송됩니다.

이 문장은 모든 단어의 정확한 의미를 모르면 kludgey하고 구문 분석하기 어렵습니다. 도움!

  • "동적로드 및 정적으로 링크 됨"이란 무엇을 의미합니까? 무언가를 동적으로로드하고 정적으로 연결할 수 있습니까? 아니면 상호 배타적입니까?

  • "... 새로로드 된 클래스 또는 범주가 응답 할 수있는 메서드를 구현합니다."어떤 메서드? 어떻게 반응합니까?


+ initialize에 관해서는 문서에 다음과 같이 나와 있습니다.

초기화는 클래스 당 한 번만 호출됩니다. 클래스 및 클래스 범주에 대해 독립적 인 초기화를 수행하려면로드 메서드를 구현해야합니다.

"클래스를 설정하려는 경우 ... 초기화를 사용하지 마십시오."라는 의미입니다. 좋아. 언제 또는 왜 초기화를 재정의합니까?

답변:


184

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보낸 후 클래스 객체에 메시지를 보냅니다 . 그러나 자신의 실행 파일에서 어떤 다른 클래스가 아직 수신되었는지 알 수 없습니다 .loadloadload

프로세스가 주소 공간에로드하는 모든 클래스는 프로세스가 클래스를 다른 용도로 사용하는지 여부에 관계없이 메소드를 load구현하면 메시지 를 수신합니다 load.

당신은 런타임이 조회하는 방법을 볼 수 있습니다 load의 특별한 경우로 방법 _class_getLoadMethodobjc-runtime-new.mm, 그리고에서 직접 호출 call_class_loadsobjc-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_initializeobjc-initialize.mmobjc_msgSend

추가 읽기

이 주제에 대한 Mike Ash의 금요일 Q & A 를 확인하십시오 .


25
당신은주의해야한다 +load범주에 대해 개별적으로 전송됩니다; 즉, 클래스의 모든 범주에는 자체 +load메서드 가 포함될 수 있습니다 .
Jonathan Grynspan

1
또한 초기화되지 않은 엔터티에 대한 참조로 인해 필요한 경우 메서드에 initialize의해 올바르게 호출됩니다 . 이것은 (이상하지만 현명하게) 이전 에 실행될 수 있습니다 ! 어쨌든 그것은 내가 관찰 한 것입니다. 이것은 "그리고 당신이를받을 때까지, 당신의 과정에있는 모든 수업은 이미 (적절하다면) 받았을 것입니다."와 반대되는 것 같습니다 . loadloadinitializeloadinitializeload
Benjohn 2014 년

5
load먼저 받습니다 . 그런 다음 계속 실행되는 initialize동안 수신 할 수 있습니다 load.
rob mayoff

1
@robmayoff는 각각의 메서드 안에 [super initialize] 및 [super load] 줄을 추가 할 필요가 없습니까?
damithH

1
이는 일반적으로 나쁜 생각입니다. 왜냐하면 런타임이 이미 두 메시지를 모두 사용자에게 보내기 전에 모든 수퍼 클래스에 보냈기 때문입니다.
rob mayoff

17

그것이 의미하는 바는 +initialize카테고리에서 재정의하지 않는다는 것입니다. 아마도 무언가를 깨뜨릴 것입니다.

+load클래스 또는 구현하는 카테고리에 한 번이라고 +load, 가능한 한 빨리 그 클래스 또는 카테고리가로드됩니다. "정적으로 연결됨"이라는 것은 앱 바이너리로 컴파일되었음을 의미합니다. +load이렇게 컴파일 된 클래스 의 메서드는 앱이 시작될 때, 아마도 main(). "동적으로로드 됨"이라고 표시되면 플러그인 번들 또는에 대한 호출을 통해로드됨을 의미합니다 dlopen(). iOS를 사용하는 경우이 경우를 무시할 수 있습니다.

+initialize메시지를 처리하기 직전에 클래스에 메시지가 처음 전송 될 때 호출됩니다. 이것은 (분명히) 한 번만 발생합니다. +initialize카테고리에서 재정의하면 다음 세 가지 중 하나가 발생합니다.

  • 카테고리 구현이 호출되고 클래스의 구현이
  • 다른 사람의 카테고리 구현이 호출됩니다. 당신이 쓴 것은 아무것도하지 않습니다
  • 카테고리가 아직로드되지 않았고 해당 구현이 호출되지 않습니다.

이것이 +initialize카테고리에서 재정의해서는 안되는 이유 입니다. 사실 어떤 항목을 대체하는지 또는 자신의 대체 항목이 다른 카테고리에 의해 자체적으로 전환 될지 확실하지 않기 때문에 카테고리의 메소드를 바꾸는 것은 매우 위험합니다 .

BTW, 고려해야 할 또 다른 문제 +initialize는 누군가가 당신을 하위 클래스로 만들면 잠재적으로 클래스에 대해 한 번, 각 하위 클래스에 대해 한 번씩 호출 될 수 있다는 것입니다. static변수 설정과 같은 작업을 수행하는 경우을 사용 dispatch_once()하거나 테스트 하여이를 방지하고 싶을 것 self == [MyClass class]입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.