Objective-C에서 self = [super init]가 0이 아닌지 확인해야하는 이유는 무엇입니까?


165

Objective-C에서 init 메소드 작성에 대한 일반적인 질문이 있습니다.

초기화를 계속하기 전에 init 메소드가 self = [super init]가 아닌지 확인 해야하는 곳은 어디에서나 (Apple의 코드, 서적, 오픈 소스 코드 등) 알 수 있습니다.

init 메소드의 기본 Apple 템플리트는 다음과 같습니다.

- (id) init
{
    self = [super init];

    if (self != nil)
    {
        // your code here
    }

    return self;
}

왜?

init이 언제 nil을 반환 할 것인가? NSObject에서 init를 호출하고 다시 반환하면 실제로 무언가를 망쳐 놓아야합니까? 이 경우 프로그램을 작성하지 않을 수도 있습니다.

클래스의 init 메소드가 nil을 반환하는 것이 일반적입니까? 그렇다면 어떤 경우에 왜 그런가?


1
Wil Shipley는 이것과 관련된 기사를 다시 게시했습니다. [self = [stupid init];] ( wilshipley.com/blog/2005/07/self-stupid-init.html ) 댓글도 읽어보세요.
Ryan Townshend

6
당신은 Wil Shipley 또는 Mike Ash 또는 Matt Gallagher 에게 물을 수 있습니다. 어느 쪽이든, 그것은 토론 주제입니다. 그러나 일반적으로 애플의 관용구를 고수하는 것이 좋습니다 ... 결국 그들의 프레임 워크입니다.
jbrennan

1
Wil은 [super init]가 수신자를 반환하지 않을 수도 있다는 것을 알고, 맹목적으로 맹목적으로 자기를 재할 당하지 않는 사례를 더 많이 만든 것으로 보인다.
Jasarien

3
Wil은 그 포스트가 처음 작성된 이후로 그의 생각을 바꿨습니다.
bbum

5
나는이 질문을 얼마 전에 보았고 다시 발견했습니다. 완전한. +1
Dan Rosenstark

답변:


53

예를 들면 다음과 같습니다.

[[NSData alloc] initWithContentsOfFile:@"this/path/doesn't/exist/"];
[[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"];
[NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];

... 등등. (참고 : 파일이 존재하지 않으면 NSData에서 예외가 발생할 수 있습니다). nil을 리턴하는 것이 문제가 발생할 때 예상되는 동작이있는 영역이 상당히 많기 때문에 일관성을 위해 nil을 거의 항상 점검하는 것이 표준 관행입니다.


10
예, 그러나 이것은 해당 클래스의 init 메소드 안에 없습니다. NSData는 NSObject에서 상속합니다. NSData는 [super init]가 nil을 반환하는지 확인합니까? 그것이 내가 여기서 묻는 것입니다. 내가 명확하지 않으면 죄송합니다 ...
Jasarien

9
해당 클래스를 서브 클래 싱하는 경우 [super init]가 nil을 반환 할 가능성이 매우 높습니다. 모든 클래스가 NSObject의 직접적인 서브 클래스 인 것은 아닙니다. nil을 반환하지 않는다는 보장은 없습니다. 일반적으로 권장되는 방어 적 코딩 관행입니다.

7
NSObject init이 어떤 경우에도 nil을 반환 할 수 있는지 의심합니다. 메모리가 부족하면 alloc이 실패하지만 성공하면 init이 실패 할 것입니다. NSObject에는 클래스를 제외한 인스턴스 변수가 없습니다. GNUStep에서는 "반환"으로 구현되었으며 Mac에서의 디스 어셈블 링도 동일하게 보입니다. 물론이 모든 것은 부적절합니다. 표준 관용구를 따르기 만하면 할 수 있는지 아닌지를 걱정할 필요가 없습니다.
Peter N Lewis

9
모범 사례를 따르지 않기로 결심하지 않았습니다. 그러나 나는 그들이 왜 모범 사례인지 알고 싶습니다. 탑에서 뛰어 내리라는 말을 듣는 것과 같습니다. 이유를 모를 경우에만 진행하지 마십시오. 큰 현금 보상으로 바닥에 착륙 할 크고 부드러운 베개가 있습니까? 내가 점프 할 줄 알았다면 그렇지 않다면, 그렇지 않을 것입니다. 내가 왜 그것을 따르고 있는지 모르고 연습을 맹목적으로 따르고 싶지 않습니다.
Jasarien

4
alloc이 nil을 리턴하면, init는 nil로 보내지며, 항상 nil이되며, 이는 nil이됩니다.
T.

50

이 특정 관용구는 모든 경우에 작동하기 때문에 표준입니다.

흔하지는 않지만 다음과 같은 경우가 있습니다.

[super init];

... 다른 인스턴스를 반환하므로 자체 할당이 필요합니다.

그리고 nil을 반환하는 경우가 있으므로 nil 확인이 필요하므로 코드가 더 이상 존재하지 않는 인스턴스 변수 슬롯을 초기화하지 않습니다.

결론은 사용하기 위해 문서화 된 올바른 패턴이며, 사용하지 않으면 잘못하고 있다는 것입니다.


3
이것은 nullability 지정자에 비추어 여전히 사실입니까? 내 슈퍼 클래스의 이니셜 라이저가 null이 아닌 경우 여전히 확인해야 할 추가 혼란이 있습니까? (NSObject 자체는 -init실패한 것으로 보이지만…)
natevw

[super init]직접 수퍼 클래스가있을 때 nil을 반환 하는 경우 가 NSObject있습니까? "모든 것이 깨지는"경우가 아닌가?
Dan Rosenstark

1
@DanRosenstark NSObject직접 슈퍼 클래스가 아닌가 . 그러나 ... NSObject직접 슈퍼 클래스로 선언하더라도 런타임에 NSObject구현 init이 실제로 호출 되는 것이 아닌 것을 수정했을 수 있습니다 .
범블

1
너무 많은 @bbum 덕분에 버그 수정에 도움이되었습니다. 몇 가지를 배제하는 것이 좋습니다!
Dan Rosenstark

26

대부분의 클래스에서 [super init]의 반환 값이 nil이고 표준 사례에서 권장하는대로 확인한 다음 nil 인 경우 조기에 반환하면 기본적으로 앱이 여전히 올바르게 작동하지 않습니다. 당신도 그 불구하고, 그것에 대해 생각하는 경우 경우 (자기! = 전무) 검사 클래스의 적절한 작동을 위해, 거기, 당신이 실제로 시간의 99.99 % nil이 아닌 될 필요 자체를. 자, 어떤 이유에서든 [super init] nil을 반환했다고 가정합니다. 기본적으로 nil에 대한 검사는 기본적으로 클래스 호출자에게 벅을 넘겨주는 것으로 가정합니다. 성공한.

기본적으로, 내가 얻는 것은 99.99 %의 시간입니다 .if (self! = nil)는 벅을 전달자에게 넘겨주기 때문에 견고성 측면에서 아무것도 사지 않습니다. 실제로이 문제를 강력하게 처리하려면 실제로 전체 통화 계층 구조를 확인해야합니다. 그럼에도 불구하고 구매할 수있는 유일한 것은 앱이 더 깨끗하고 견고하게 실패한다는 것입니다. 그러나 여전히 실패 할 것입니다.

라이브러리 클래스가 [super init]의 결과로 임의로 nil을 반환하기로 결정했다면 어쨌든 거의 겁이 나고 라이브러리 클래스 작성자가 구현 실수를 한 것입니다.

앱이 훨씬 제한된 메모리에서 실행될 때 이것은 레거시 코딩 제안에 가깝습니다.

그러나 C 레벨 코드의 경우 일반적으로 여전히 NULL 포인터에 대해 malloc ()의 반환 값을 확인합니다. 반면 Objective-C의 경우, 반대로 증거를 찾을 때까지 if (self! = nil) 검사를 건너 뛸 것이라고 생각합니다. 왜 불일치가 발생합니까?

C 및 malloc 레벨에서 일부 경우 실제로 부분적으로 복구 할 수 있기 때문입니다. Objective-C에서 99.99 %의 경우에 [super init]가 nil을 반환하면 처리하려고해도 기본적으로 f *** ed입니다. 앱이 중단되고 여파를 처리하게 할 수도 있습니다.


6
잘 말 했어요 나는 두 번째입니다.
Roger CS Wernersson

3
+1 전적으로 동의합니다. 사소한 참고 사항 : 패턴이 할당이 더 자주 실패한 시간의 결과라고 생각하지 않습니다. 할당은 일반적으로 init가 호출 될 때 이미 수행됩니다. 할당이 실패하면 init도 호출되지 않습니다.
Nikolai Ruhe

8

이것은 위의 의견에 대한 요약입니다.

수퍼 클래스가를 반환한다고 가정 해 봅시다 nil. 무슨 일이야?

컨벤션을 따르지 않으면

init메소드 중간에 코드가 충돌합니다 . ( init의의가없는 한)

수퍼 클래스가 nil을 리턴 할 수 있다는 것을 모르고 규칙을 따르는 경우 (대부분의 사람들은 여기에서 끝납니다)

인스턴스가이므로 다른 코드가 nil예상 되기 때문에 코드가 나중에 언젠가 충돌 할 것입니다. 또는 프로그램이 충돌하지 않고 예기치 않게 작동합니다. 이런! 넌 이걸 원해? 모르겠어요 ...

규칙을 따르면 하위 클래스가 nil을 반환하도록 기꺼이 허용

코드 문서 (!)에는 "return ... or nil"이라고 명시해야하며 나머지 코드는이를 처리하기 위해 준비해야합니다. 이제 말이됩니다.


5
여기서 흥미로운 점은 옵션 # 1이 옵션 # 2 보다 분명히 선호 된다는 것입니다. 실제로 서브 클래스의 init이 nil을 리턴하기를 원하는 상황이 있다면 # 3이 선호됩니다. 코드의 버그로 인해 발생하는 유일한 이유는 # 1을 사용하십시오. 옵션 # 2를 사용하면 앱이 나중에 폭발 할 때까지 지연시켜 오류를 훨씬 더 열심히 디버깅 할 때 작업이 이루어집니다. 자동으로 예외를 잡아서 처리하지 않고 계속하는 것과 같습니다.
Mark Amery

또는 신속하게 전환하고 옵션 만 사용하십시오.
AndrewSB

7

일반적으로 수업이에서 직접 파생되는 경우 NSObject필요하지 않습니다. 그러나 클래스가 다른 클래스에서 파생되는 경우 이니셜 라이저가을 반환 할 수 있고이 nil경우 이니셜 라이저가이를 캡처하여 올바르게 작동 할 수 있습니다.

그리고 네, 기록을 위해, 나는 모범 사례를 따르고 모든 수업, 심지어에서 직접 파생 된 수업에 쓰십시오 NSObject.


1
이를 염두에두고 변수를 초기화 한 후 함수를 호출하기 전에 nil을 확인하는 것이 좋습니다. 예Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}
dev_does_software

에서 상속 NSObject는 보장하지 않는 것은 -init당신을 제공합니다 NSObject(수익률 그누 스텝의 이전 버전처럼 이국적인 런타임을 계산하면, 너무 GSObject그래서 아무 체크하고 할당 무엇을, 상관).
Maxthon Chan

3

당신은 맞습니다, 당신은 종종 글을 쓸 수 [super init]는 있지만, 그것은 단지 아무것도의 서브 클래스에서 작동하지 않을 것입니다. 사람들은 단지 가끔 필요의 경우에도, 단지 시간 코드의 하나 개의 표준 라인을 암기하고 모두를 사용하는 것을 선호하고, 따라서 우리는 표준 얻을 if (self = [super init])반환 전무의 가능성과 이외의 객체의 가능성을 모두 소요 self반환을 계정에.


3

흔한 실수는

self = [[super alloc] init];

서브 클래스 생성자 / 초기화에서 원하지 않는 수퍼 클래스의 인스턴스를 반환합니다. 서브 클래스 메소드에 응답하지 않는 오브젝트를 다시 얻습니다. 혼동 될 수 있으며, 메소드 나 ID를 찾을 수없는 응답에 대한 혼란스러운 오류가 발생합니다.

self = [super init]; 

서브 클래스의 멤버를 설정하기 전에 슈퍼 클래스에 멤버 (변수 또는 다른 객체)가 먼저 초기화되어야하는 경우 필요합니다 . 그렇지 않으면 objc 런타임은 그것들을 모두 0 또는 nil 로 초기화합니다 . ( ANSI C와 달리 메모리 청크를 전혀 지우지 않고 할당하는 경우가 많습니다 )

그렇습니다. 메모리 부족 오류, 구성 요소 누락, 리소스 획득 실패 등으로 인해 기본 클래스 초기화에 실패 할 수 있으므로 nil 검사는 현명하며 몇 밀리 초도 걸리지 않습니다.


2

이것은 intialazation이 작동했는지 확인하고, init 메소드가 nil을 리턴하지 않으면 if 문이 true를 리턴하므로 오브젝트 작성을 점검하는 방법이 올바르게 작동했습니다. 내가 init이 실패 할 수도 있다고 생각할 수있는 몇 가지 이유는 아마도 슈퍼 클래스가 알지 못하는 재정의 된 init 메소드 나 그 종류의 무언가 일 것입니다. 그러나 그것이 발생하면 충돌이 일어날 것이라고 생각하지 않아서 항상 점검해야합니다.


그러나 htey는 함께 불려집니다. 할당이 실패하면 어떻게됩니까?
Daniel

alloc이 실패하면 init을 호출하는 클래스의 인스턴스가 아닌 nil로 전송됩니다. 이 경우 아무 일도 일어나지 않으며 [super init]가 nil을 반환했는지 여부를 테스트하는 코드도 실행되지 않습니다.
Jasarien

2
메모리 할당이 항상 + alloc에서 수행되는 것은 아닙니다. 클래스 클러스터의 경우를 고려하십시오. NSString은 이니셜 라이저가 호출 될 때까지 사용할 특정 서브 클래스를 알지 못합니다.
bbum

1

OS X에서는 -[NSObject init]메모리 이유로 인해 실패 할 가능성이 없습니다 . iOS에서도 마찬가지입니다.

또한 nil어떤 이유로 든 리턴 될 수있는 클래스를 서브 클래 싱 할 때 작성하는 것이 좋습니다 .


2
iOS 및 맥 OS 모두에서 -[NSObject init]입니다 매우 그것이 어떤 메모리를 할당하지 않는 메모리 이유로 인해 실패 할 가능성.
Nikolai Ruhe

나는 그가 init가 아니라 alloc을 의미한다고 생각한다 :)
Thomas Tempelmann
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.