본문 바로가기
프로그래밍 놀이터/iOS

[Effective Objective-C] #51 initialize 와 load 메서드는 간결하게 만들라

by 돼지왕 왕돼지 2017. 10. 19.
반응형

 [Effective Objective-C] #51 initialize 와 load 메서드는 간결하게 만들라


출처 : Effective Objective-C

category load, class category load, Dynamic Loading, fragile state, Hang, initialize, Integrity, IOS, load, load phase, load 구현, load 메서드 내 다른 class 이용, mainthread, normal state, nsobject, OSX, override, uithread, [Effective Objective-C] #51 initialize 와 load 메서드는 간결하게 만들라, 다른 클래스 사용, 디버깅, 락, 런타임, 런타임 갱신, 런타임 무결성, 런타임 손상, 런타임 추가, 로드, 로드 단계, 메서드, 메서드 호출, 상속, 상위 클래스, 스레드 안전, 앱 응답 불능, 앱 중단, 앱 처음 실행, 원하는 클래스 초기화 작업, 의존 라이브러리, 전역 상태 설정 초기화, 전역 상태값 initialize, 지연 호출, 직접 호출, 처음 사용, 초기화, 카테고리, 컴파일 에러, 클래스


-

오브젝티브-C 에서 최상위 클래스인 NSObject 를 상속한 대다수 클래스에는 초기화를 할 수 있는 몇 가지 메서드가 있다.

이 메서드들 중 첫 번째는 load 이고 프로토타입은 다음과 같다.

+ (void)load



-

클래스와 카테고리가 런타임에 추가될 때 이 메서드가 딱 한 번 호출된다.

이는 클래스 또는 카테고리를 포함하는 라이브러리가 로드될 때, 즉 일반적으로 앱이 처음 실행(launch)될 때 일어난다.

하지만 iOS 용 앱일 때만 해당된다.

맥 OS X 앱은 동적 로딩(dynamic loading)같은 기능을 자유롭게 쓸 수 있다.

그렇기 때문에 앱이 실행된 후에 라이브러리가 로딩될 수도 있다.

카테고리의 load 메서드는 카테고리가 포함된 클래스의 load 메서드가 호출된 후에 호출된다.



-

load 메서드가 실행되는 순간은 런타임이 손상되기 쉬운 상태(fragile state)가 된다는 문제가 있다.

모든 상위 클래스의 load 메서드는 다른 어떤 클래스의 메서드들보다 먼저 실행되는 것이 보장된다.

또한 의존 라이브러리 클래스의 모든 load 메서드들도 먼저 실행되는 것이 보장된다.

그러나 주어진 라이브러리 클래스들의 로드 순서는 무작위다.

그렇기 때문에 load 메서드 내에서 다른 클래스를 사용하는 것은 안전하지 않다.



-

load 가 정상적인 메서드 상속 규칙을 다르지 않는다는 사실을 알고 있어야 한다.

load 를 구현하지 않는 클래스는 그것의 상위 클래스가 load 를 구현했든 말든 호출하지 않는다.

또한 load 는 카테고리와 클래스 자체에 모두 있을 수도 있다.

두 구현 모두 호출될 것이다.

카테고리의 load 가 호출되기 전에 클래스의 것부터 호출될 것이다.



-

반드시 load 구현을 최소로 해야 한다.

이는 가능한 한 적은 일을 하게 만드는 것을 의미한다.

로딩되는 동안 전체 앱이 중단되기 때문이다.

load 메서드가 무거운 작업을 하면 그동안 앱은 응답 불능 상태가 될 것이다.

어떠한 락도 잡으려 하지 말고 락을 잡는 어떠한 메서드도 호출해서는 안 된다.

본질적으로 매우 적은 일을 해야 한다.

사실 대부분 load 는 클래스가 사용되기 전에 필요한 일을 수행하는 적절한 곳이 아니다.

이곳은 사실은 디버깅을 위한 곳이다.

카테고리를 사용했을 때 그 카테고리가 성공적으로 로딩되었는지 검사할 때 사용될 수 있다.

이 메서드가 과거부터 사용되어 왔지만 최신 오브젝티브-C 코드 베이스의 거의 대부분은 load 가 필요 없다고 해도 과언이 아니다.



-

클래스를 초기화하는 또 다른 방법은 initialize 메서드를 재정의하는 것이다.

+ (void)initialize


모든 클래스는 사용되기 전에 이 메서드가 딱 한 번 호출된다.

이 메서드는 런타임이 호출하고 절대로 직접 호출되지 않는다.

이 메서드는 load 와 비슷하지만 미묘하고 중요한 차이점이 있다.


첫 번째로 이 메서드는 지연되어 호출된다.

이는 클래스가 처음 사용되기 바로 전에 호출된다는 것이다.

그래서 한 번도 사용되지 않는 클래스에선 initialize 메서드가 절대로 실행되지 않는다.

그러나 모든 일을 완료하기 전까지 앱을 중단하는 load 와는 다르게 initialize 는 실행에 필요한 특정 기간이 없다.


load 와 다른 두 번째 차이점은 메서드가 실행되는 동안 런타임이 평소와 같은 상태 ( normal state) 라는 것이다.

그렇기 때문에 런타임 무결성(integrity) 관점에서 어떠한 클래스의 어떠한 메서드를 사용하거나 호출해도 안전하다.

또한 런타임은 initialize 가 스레드 안전한 환경에서 실행되는 것을 보장한다.

이는 오직 initialize 를 실행하는 스레드만이 그 클래스와 클래스의 인스턴스와 상호 작용 할 수 있음을 말한다.

다른 클래스는 initialize 가 완료될 때까지 중단된다.


마지막 차이점은 initialize 가 다른 메시지들처럼 보내진다는 것이다.

이 메서드를 구현하지 않아도 상위 클래스가 구현을 했다면 initializer 메서드는 실행될 것이다.

이는 당연하게 들리지만 간과될 때가 있다.



-

+ (void)initialize{

     if ( self == [EOCBaseClass class] ){

          NSLog(@“%@ initialized”, self);

     }

}


이 메서드에서 이러한 검사를 하면 원하는 클래스에서만 초기화 작업이 되게 할 수 있다.



-

load 와 initialize 두 메서드는 모두 간결해야 한다.

이 메서드들은 클래스가 정확하게 동작하는 데 필요한 상태값들을 초기화하는 일만 해야 하고 너무 오래 걸리거나 락을 잡는 일을 하지 말아야 한다.

load 의 경우는 그 이유를 앞에서 다루었다.

initialize 에서도 비슷한 이유 때문이다.

첫 번째로 누구도 앱이 행(hang) 걸리는 것을 원하지 않는다.

클래스가 최초로 사용될 때 초기화 될 것이다.

그리고 이는 어떠한 스레드에서도 가능하다.

UI 스레드에서 최초로 클래스를 사용했다면 앱이 초기화 완료될 때까지 중단될 것이다.

이는 앱이 응답하지 않는 결과를 낳는다.

어떠한 클래스가 언제 최초로 사용될지 예측하는 것은 보통은 어렵다.

그렇다고 강제로 특정 스레드가 클래스를 초기화하도록 강제하는 것도 분명 이상적이지 않다.



-

클래스가 초기화 될 때를 프로그래머가 모두 정하는 것이 아니다.

클래스가 최초로 사용되기 전에 초기화되는 것은 보장되지만 주어진 시간에 반드시 초기화된다고 믿는 것은 위험하다.

나중에 런타임이 갱신되어 클래스를 초기화하는 방법이 바뀔 수 있기 때문이다.

그러면 여러분이 예상하는 클래스의 초기화 시점이 유효하지 않게 될 것이다.



-

initialize 를 복잡하게 구현했다면 해당 클래스에서 다른 클래스들을 직간접적으로 사용하며 문제가 생길 수 있다.

이러한 클래스들이 아직 초기화되지 않았다면 이 때 강제로 초기화된다.

initialize 의 목적이 내부 데이터를 설정하는 것이기 때문에 initialize 내부에서는 호출한 메서드가 자신의 것이라 할지라도 다른 메서드들을 호출하지 말아야 한다.

initialize 에서 다른 메서드를 호출했고 나중에 그 메서드에서 initialize 를 호출하도록 구현한다면 문제들이 반드시 나타날 것이다.



-

전역 상태를 설정하는 초기화는 컴파일 시간에 초기화 할 수 없다는 사실을 기억하라.

다음과 같이 시도하면 컴파일러가 에러를 일으킬 것이다.

static NSMutableArray *kSomeObjects = [NSMutableArray new];



-

전역 상태를 초기화하는 것보다 더 많은 것이 필요하면, 이를 수행하는 전용 메서드를 생성하는 것을 고려하라.

그리고 사용자가 그 클래스를 사용하기 전에 이 전용 메서드를 호출하는 것을 강제하도록 하라.

이런 예는 최초로 접근했을 때 추가적인 일을 수행하는 싱글턴 클래스에서 볼 수 있다.




기억할 점


-

클래스가 load 메서드를 구현했고 그 메서드가 호출되면 클래스는 로드 단계(load phase)로 진입한다.

이 메서드는 또한 카테고리에도 있을 수 있다.

그것으로 인해 클래스는 항상 카테고리가 로드되기 전에 로드된다.

load 메서드는 다른 메서드들이 지키는 일반적인 재정의 방침을 따르지 않는다.



-

클래스가 최초로 사용되기 전에 initialize 메서드가 호출된다.

이 메서드는 일반적인 재정의 규칙을 따른다.

그래서 이 메서드는 클래스가 초기화되었는지 확인하는 최적의 장소다.



-

load 와 initialize, 이 두 메서드는 모두 간결하게 유지해야 한다.

그렇게 하면 앱이 계속 응답을 잘 하고 상호 참조가 발생할 가능성을 줄이는 데 도움이 된다.



-

컴파일 시간에 할 수 없는 전역 상태값을 설정하는 것은 initialize 메서드에서 하라.




반응형

댓글