[objective-c] Objective-C에서 nil에 메시지 보내기

Apple의 Objective-C 2.0 문서를 읽고있는 Java 개발자로서 ” 메시지를 nil로 전송 “이 의미 하는 바가 무엇인지 궁금 합니다. 실제로 유용하다는 것은 말할 것도 없습니다. 문서에서 발췌 :

Cocoa에는이 사실을 활용하는 몇 가지 패턴이 있습니다. 메시지에서 nil로 반환 된 값도 유효 할 수 있습니다.

  • 메서드가 객체, 포인터 유형, sizeof (void *)보다 작거나 같은 크기의 정수 스칼라, float, double, long double 또는 long long을 반환하면 nil로 전송 된 메시지는 0을 반환합니다. .
  • 메서드가 Mac OS X ABI 함수 호출 가이드에 정의 된대로 레지스터에 반환되는 구조체를 반환하는 경우 nil로 전송 된 메시지는 데이터 구조의 모든 필드에 대해 0.0을 반환합니다. 다른 구조체 데이터 유형은 0으로 채워지지 않습니다.
  • 메서드가 앞서 언급 한 값 유형 이외의 것을 반환하는 경우 nil로 전송 된 메시지의 반환 값은 정의되지 않습니다.

Java가 위의 설명을 무시할 수 없도록 내 두뇌를 만들었습니까? 아니면 이것을 유리처럼 선명하게 만드는 내가 놓친 것이 있습니까?

Objective-C에서 메시지 / 수신자에 대한 아이디어를 얻었습니다. 수신자가 nil.



답변

글쎄요, 매우 인위적인 예를 사용하여 설명 할 수 있다고 생각합니다. ArrayList의 모든 요소를 ​​출력하는 Java 메서드가 있다고 가정 해 보겠습니다.

void foo(ArrayList list)
{
    for(int i = 0; i < list.size(); ++i){
        System.out.println(list.get(i).toString());
    }
}

이제 그 메서드를 다음과 같이 호출하면 : someObject.foo (NULL); 목록에 액세스하려고 할 때 NullPointerException이 발생합니다.이 경우 list.size (); 이제는 NULL 값을 사용하여 someObject.foo (NULL)를 호출하지 않을 것입니다. 그러나 someObject.foo (otherObject.getArrayList ());와 같은 ArrayList 생성 오류가 발생하면 NULL을 반환하는 메서드에서 ArrayList를 얻었을 수 있습니다.

물론 다음과 같은 작업을 수행하면 문제가 발생합니다.

ArrayList list = NULL;
list.size();

이제 Objective-C에는 동일한 방법이 있습니다.

- (void)foo:(NSArray*)anArray
{
    int i;
    for(i = 0; i < [anArray count]; ++i){
        NSLog(@"%@", [[anArray objectAtIndex:i] stringValue];
    }
}

이제 다음 코드가 있다면 :

[someObject foo:nil];

Java가 NullPointerException을 생성하는 것과 동일한 상황이 있습니다. nil 객체는 [anArray count]에서 먼저 액세스됩니다. 그러나 NullPointerException을 던지는 대신 Objective-C는 위의 규칙에 따라 단순히 0을 반환하므로 루프가 실행되지 않습니다. 그러나 설정된 횟수만큼 실행되도록 루프를 설정하면 먼저 [anArray objectAtIndex : i]에서 anArray에 메시지를 보냅니다. 이것은 또한 0을 반환하지만 objectAtIndex :는 포인터를 반환하고 0에 대한 포인터는 nil / NULL이므로 NSLog는 루프를 통해 매번 nil로 전달됩니다. (NSLog는 메소드가 아니라 함수이지만 nil NSString을 전달하면 (null) 출력합니다.

어떤 경우에는 NullPointerException이있는 것이 더 좋습니다. 프로그램에 문제가 있음을 즉시 알 수 있기 때문에 예외를 포착하지 않으면 프로그램이 충돌합니다. (C에서 이러한 방식으로 NULL을 역 참조하려고하면 프로그램이 충돌합니다.) Objective-C에서는 대신 잘못된 런타임 동작이 발생할 수 있습니다. 그러나 0 / nil / NULL / 0으로 된 구조체를 반환하는 경우 중단되지 않는 메서드가있는 경우 개체 또는 매개 변수가 nil인지 확인하지 않아도됩니다.


답변

메시지에는 nil아무 것도 반환하지 않습니다 nil, Nil, NULL, 0, 또는 0.0.


답변

다른 모든 게시물은 정확하지만 여기서 중요한 것은 개념 일 수 있습니다.

Objective-C 메서드 호출에서 선택기를 허용 할 수있는 모든 개체 참조는 해당 선택기에 대한 유효한 대상입니다.

이렇게하면 “대상 개체가 X 유형입니까?”라는 많은 비용이 절약됩니다. 코드-수신 객체가 선택자를 구현하는 한 어떤 클래스인지 전혀 차이가 없습니다 ! nil어떤 선택을 받아 그 NSObject라는 것입니다 – 그냥하지 않습니다 것을. 이렇게하면 “무를 확인하고 참이면 메시지를 보내지 마십시오”코드도 많이 제거됩니다. ( “만약 그것이 그것을 받아들이면 그것을 구현한다”개념은 또한 일종의 자바 인터페이스와 비슷한 프로토콜 을 생성 할 수있게 해준다 . 클래스가 명시된 메소드를 구현하면 프로토콜을 준수한다는 선언이다.)

그 이유는 컴파일러를 행복하게 유지하는 것 외에는 아무것도하지 않는 원숭이 코드를 제거하기 위해서입니다. 예, 한 번 더 메서드 호출의 오버 헤드가 발생하지만 프로그래머 시간 을 절약 할 수 있습니다. . 이는 CPU 시간보다 훨씬 더 비싼 리소스입니다. 또한 애플리케이션에서 더 많은 코드와 조건부 복잡성을 제거하고 있습니다.

비추천 사용자를위한 설명 : 이것이 좋은 방법이 아니라고 생각할 수 있지만 언어가 구현되는 방식 이며 Objective-C에서 권장되는 프로그래밍 관용구 입니다 (Stanford iPhone 프로그래밍 강의 참조).


답변

의미하는 바는 objc_msgSend가 nil 포인터에서 호출 될 때 런타임이 오류를 생성하지 않는다는 것입니다. 대신 (종종 유용한) 값을 반환합니다. 부작용이있을 수있는 메시지는 아무것도하지 않습니다.

대부분의 기본값이 오류보다 더 적절하기 때문에 유용합니다. 예를 들면 :

[someNullNSArrayReference count] => 0

즉, nil은 빈 배열로 보입니다. nil NSView 참조를 숨기는 것은 아무것도하지 않습니다. 핸디, 응?


답변

문서의 인용문에는 두 가지 개별 개념이 있습니다. 문서에서 더 명확하게 설명하면 더 좋을 수 있습니다.

Cocoa에는이 사실을 활용하는 몇 가지 패턴이 있습니다.

메시지에서 nil로 반환 된 값도 유효 할 수 있습니다.

전자가 여기서 더 관련이있을 수 있습니다. 일반적으로 nil코드를보다 간단하게 만들기 위해 메시지를 보낼 수 있습니다. 모든 곳에서 null 값을 확인할 필요가 없습니다. 표준 예제는 아마도 접근 자 메서드 일 것입니다.

- (void)setValue:(MyClass *)newValue {
    if (value != newValue) {
        [value release];
        value = [newValue retain];
    }
}

메시지를 보내는 경우 nil유효하지 않았다,이 방법은 더 복잡 할 것이다 – 당신은 보장하기 위해 두 개의 추가 검사를해야 할 것 value하고 newValue없는 nil그들에게 메시지를 보내기 전에.

그러나 후자의 지점 (메시지에서 반환 된 값 nil도 일반적으로 유효 함)은 전자에 승수 효과를 추가합니다. 예를 들면 :

if ([myArray count] > 0) {
    // do something...
}

이 코드는 다시 한 번 확인할 필요가 없습니다. nil 값을 자연스럽게 흐릅니다.

이 모든 것이 말하면, 메시지를 보낼 수있는 추가적인 유연성은 nil비용이 듭니다. 값이 nil. 일 수있는 가능성을 고려하지 않았기 때문에 특정 단계에서 특이한 방식으로 실패하는 코드를 작성할 가능성이 있습니다 .


답변

에서 그렉 파커사이트 :

LLVM 컴파일러 3.0 (Xcode 4.2) 이상을 실행하는 경우

반환 유형으로 nil에 대한 메시지 | 반환
최대 64 비트의 정수 | 0
long double까지의 부동 소수점 | 0.0
포인터 | 무
구조체 | {0}
모든 _ 복합 유형 | {0, 0}


답변

이는 안전을 위해 모든 곳에서 nil 객체를 확인할 필요가 없다는 것을 의미합니다. 특히 :

[someVariable release];

또는 언급했듯이 다양한 count 및 length 메서드는 nil 값이있을 때 모두 0을 반환하므로 nil에 대한 추가 검사를 추가 할 필요가 없습니다.

if ( [myString length] > 0 )

아니면 이거:

return [myArray count]; // say for number of rows in a table