Objective-C에서 nullable, __nullable 및 _Nullable의 차이점


154

Xcode 6.3에는 Objective-C 에서 API의 의도를 더 잘 표현 하기 위해 (그리고 물론 더 나은 Swift 지원을 보장하기 위해) 새로운 주석이 도입되었습니다 . 이러한 주석은 물론 있었다 nonnull, nullable하고 null_unspecified.

그러나 Xcode 7에서는 다음과 같은 많은 경고가 나타납니다.

포인터에 널 입력 가능 유형 지정자가 없습니다 (_Nonnull, _Nullable 또는 _Null_unspecified).

그 외에도 Apple은 다른 유형의 Null 허용 지정자를 사용하여 C 코드 ( source )를 표시합니다.

CFArrayRef __nonnull CFArrayCreate(
CFAllocatorRef __nullable allocator, const void * __nonnull * __nullable values, CFIndex numValues, const CFArrayCallBacks * __nullable callBacks);

요약하자면, 이제 다음과 같은 3 가지 널링 어노테이션이 있습니다.

  • nonnull, nullable,null_unspecified
  • _Nonnull, _Nullable,_Null_unspecified
  • __nonnull, __nullable,__null_unspecified

왜 어떤 위치에서 어떤 주석을 사용해야하는지 알고 있지만 어떤 유형의 주석을 사용해야하는지, 어디서, 왜 혼란스러워하고 있습니다. 이것이 내가 수집 할 수있는 것입니다.

  • 내가 사용한다 속성 nonnull, nullable, null_unspecified.
  • 내가 사용해야 메서드 매개 변수의 경우 nonnull, nullable, null_unspecified.
  • 내가 사용한다 C 방법의 경우 __nonnull, __nullable, __null_unspecified.
  • 그런 내가 사용한다 이중 포인터 등의 경우에 대해서는 _Nonnull, _Nullable, _Null_unspecified.

그러나 나는 왜 우리가 기본적으로 같은 일을하는 많은 주석이 있는지에 대해 여전히 혼란 스럽습니다.

그래서 내 질문은 :

이러한 주석의 정확한 차이점은 무엇이며 올바르게 배치하는 방법과 이유는 무엇입니까?


3
나는 그 게시물을 읽었지만 차이점과 왜 우리가 지금 3 가지 유형의 주석을 가지고 있는지 설명하지 않고 3 번째 유형을 추가 한 이유를 정말로 알고 싶습니다.
Legoless

2
이것은 @ Cy-4AH에 실제로 도움이되지 않으며 그것을 알고 있습니다. :)
Legoless

@Legoless, 잘 읽어 보시겠습니까? 더 나은 가독성, 호환성 이유 등을 위해 다른 것을 사용할 수있을 때 사용 범위와 감사 범위, 감사 범위는 무엇인지 정확하게 설명합니다 ... 실제로 물어보고 싶은 것을 알지 못할 수도 있지만 대답은 분명히 링크 아래에 있습니다. 어쩌면 나일지도 모르지만, 그들의 목적을 설명하고 여기에 간단한 설명을 복사하여 붙여 넣는 데 더 이상의 설명이 필요하다고 생각하지 않습니다. :(
holex

2
특정 부분을 정리하지만 아니요, 왜 우리가 첫 번째 주석을 가지고 있지 않은지 이해하지 못합니다. 왜 __nullable에서 _Nullable로 갔는지 설명하지만 nullable이있는 경우 왜 _Nullable이 필요한지 설명하지 않습니다. 또한 애플이 왜 여전히 자신의 코드에서 __nullable을 사용하는지 설명하지 않습니다.
Legoless

답변:


152

로부터 clang 문서 :

널 입력 가능 (유형) 규정자는 주어진 포인터 유형의 값이 널 ( _Nullablequalifier) ​​일 수 있는지, 널 ( qualifier)에 대해 정의 된 의미를 갖지 _Nonnull않거나 널의 목적이 명확하지 않은 ( _Null_unspecifiedqualifier) ​​여부를 나타 냅니다. . 널 입력 가능 규정자는 유형 시스템 내에서 표현되기 때문에 nonnullreturns_nonnull속성 보다 일반적 이므로 널이 아닌 포인터 배열에 대한 널 입력 가능 포인터를 표현할 수 있습니다. Nullability 한정자는 해당 포인터 오른쪽에 기록됩니다.

,

Objective-C에는 문맥에 맞지 않는 밑줄이없는 키워드를 사용하여 Objective-C 메소드 및 특성에 사용할 수있는 널 입력 가능 규정 자에 대한 대체 철자가 있습니다.

그래서 메소드가 리턴 및 사용할 수있는 매개 변수에 대한 이중 밑줄 버전 __nonnull/ __nullable/ __null_unspecified대신 중 단일 밑줄 사람의, 또는 그 대신 비 밑줄 것들. 차이점은 단일 및 이중 밑줄이있는 문자는 유형 정의 뒤에 배치해야하고 밑줄이없는 문자는 유형 정의 앞에 배치해야한다는 점입니다.

따라서 다음 선언은 동일하며 정확합니다.

- (nullable NSNumber *)result
- (NSNumber * __nullable)result
- (NSNumber * _Nullable)result

매개 변수의 경우 :

- (void)doSomethingWithString:(nullable NSString *)str
- (void)doSomethingWithString:(NSString * _Nullable)str
- (void)doSomethingWithString:(NSString * __nullable)str

속성 :

@property(nullable) NSNumber *status
@property NSNumber *__nullable status
@property NSNumber * _Nullable status

그러나 밑줄 이외의 문자는 허용되지 않으므로 void와 다른 것을 반환하는 이중 포인터 또는 블록이 포함되면 문제가 복잡해집니다.

- (void)compute:(NSError *  _Nullable * _Nullable)error
- (void)compute:(NSError *  __nullable * _Null_unspecified)error;
// and all other combinations

매개 변수로 블록을 허용하는 메소드와 유사하게, nonnull/ nullable한정자가 리턴 유형이 아닌 블록에 적용되므로 다음과 같습니다.

- (void)executeWithCompletion:(nullable void (^)())handler
- (void)executeWithCompletion:(void (^ _Nullable)())handler
- (void)executeWithCompletion:(void (^ __nullable)())handler

블록에 반환 값이 있으면 밑줄 버전 중 하나를 사용해야합니다.

- (void)convertObject:(nullable id __nonnull (^)(nullable id obj))handler
- (void)convertObject:(id __nonnull (^ _Nullable)())handler
- (void)convertObject:(id _Nonnull (^ __nullable)())handler
// the method accepts a nullable block that returns a nonnull value
// there are some more combinations here, you get the idea

결론적으로, 컴파일러가 한정자를 할당 할 항목을 결정할 수있는 한 둘 중 하나를 사용할 수 있습니다.


2
밑줄 버전은 어느 곳에서나 사용할 수있는 것 같습니다. 따라서 어떤 곳에서는 밑줄이없는 버전을 사용하고 다른 곳에서는 밑줄이없는 버전을 사용하지 않고 계속 사용할 것이라고 생각합니다. 옳은?
Vaddadi Kartick

@KartickVaddadi 예, 맞습니다. 단일 밑줄 또는 이중 밑줄 버전을 일관되게 사용할 수 있습니다.
Cristik

_Null_unspecified스위프트에서 이것은 선택 사항으로 해석됩니까? 비 선택적 또는 무엇?
Honey

1
@Honey _Null_unspecified는 스위프트에서 암시 적으로 래핑되지 않은 옵션으로 가져옵니다
Cristik

1
@Cristik aha. 나는 그것이 기본값이라고 생각합니다 ... 지정 하지 않았을 때 암시 적으로 래핑되지 않은 옵션을 얻었습니다 ...
Honey

28

에서 스위프트 블로그 :

이 기능은 키워드 __nullable 및 __nonnull과 함께 Xcode 6.3에서 처음 릴리스되었습니다. 타사 라이브러리와의 잠재적 충돌로 인해 Xcode 7의 라이브러리가 _Nullable 및 _Nonnull로 변경되었습니다. 그러나 Xcode 6.3과의 호환성을 위해 매크로 __nullable 및 __nonnull을 사전 정의하여 새 이름으로 확장했습니다.


4
간단히 말해서 단일 및 이중 밑줄 버전은 동일합니다.
Vaddadi Kartick

4
또한 Xcode 7.0 릴리스 정보에 문서화되어 있습니다. "이중 밑줄이있는 널 입력 가능 규정 자 (__nullable, __nonnull 및 __null_unspecified)는 각각 대문자가있는 단일 밑줄 (_Nullable, _Nonnull 및 _Null_unspecified)을 사용하도록 이름이 바뀌 었습니다. 컴파일러는 매크로를 사전 정의합니다. 지정되지 않은 이전의 이중 지정되지 않은 이름에서 소스 호환성을위한 새로운 이름으로의 매핑 (21530726) "
Cosyn

25

나는 이 기사를 정말로 좋아 했기 때문에 저자가 쓴 것을 보여주고있다 : https://swiftunboxed.com/interop/objc-nullability-annotations/

  • null_unspecified:Swift를 암시 적으로 래핑되지 않은 선택 사항에 연결합니다. 이것이 기본값 입니다.
  • nonnull: 값은 nil이되지 않습니다. 정규 참조에 연결합니다.
  • nullable: 값은 nil 일 수 있습니다. 선택 사항에 연결합니다.
  • null_resettable: 읽을 때 값을 절대로 nil로 지정할 수는 없지만 nil로 설정하여 재설정 할 수 있습니다. 속성에만 적용됩니다.

위의 표기법 은 속성 또는 함수 / 변수 의 컨텍스트 에서 사용하는지 여부에 따라 다릅니다 .

포인터 대 속성 표기법

이 기사의 저자는 좋은 예를 제공했습니다.

// property style
@property (nonatomic, strong, null_resettable) NSString *name;

// pointer style
+ (NSArray<NSView *> * _Nullable)interestingObjectsForKey:(NSString * _Nonnull)key;

// these two are equivalent!
@property (nonatomic, strong, nullable) NSString *identifier1;
@property (nonatomic, strong) NSString * _Nullable identifier2;

12

매우 편리합니다

NS_ASSUME_NONNULL_BEGIN 

와 함께

NS_ASSUME_NONNULL_END 

이 코드 레벨 'nullibis'의 필요성을 무효화합니다 :-)이 종류의 차종의 같은 것을 생각하는 것이 모두가 null 이외의 (또는이다 nonnull또는 _nonnull또는 __nonnull별도로 명시하지 않는 한).

불행히도 이것에도 예외가 있습니다 ...

  • typedefs로 간주 되지 않습니다 __nonnull(참고, nonnull작동하지 않는 것, 추한 오빠를 사용해야합니다)
  • id *명백한 nullibi가 필요하지만 sin-tax를 와우 _Nullable id * _Nonnull해야합니다.
  • NSError ** 항상 nullable로 가정

그래서 동일한 기능을 도출 예외에 대한 예외와 일치하지 않는 키워드로, 아마도 방법은 추악한 버전을 사용하는 것입니다 __nonnull/ __nullable/ __null_unspecified... 컴파일러가 불평 때 스왑을? 아마도 이것이 Apple 헤더에 존재하는 이유일까요?

흥미롭게도 뭔가 내 코드에 넣었습니다 ... 코드 (구식 Apple C ++ 스타일 녀석)에서 밑줄을 싫어합니다. 그래서 나는 이것을 입력하지 않았지만 등장했습니다 (몇 가지 예).

typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition,
                                          NSURLCredential * __nullable credential );

더 흥미롭게도 __nullable을 삽입 한 위치가 잘못되었습니다 ... (eek @!)

나는 밑줄이 아닌 버전을 사용할 수 있기를 정말로 원하지만 컴파일러가 오류로 표시되므로 컴파일러와 함께 비행하지 않는 것이 분명합니다.

typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition,
                                          NSURLCredential * nonnull  credential );

2
밑줄이 아닌 것은 여는 괄호 바로 뒤에 만 사용할 수 있습니다 (예 : null이 아닌 경우 ... 삶을 더 흥미롭게하기 위해 확신합니다)
Elise van Looij

null이 아닌 id를 어떻게 만드나요? 이 답변에는 많은 지식이 포함되어 있지만 명확성이 부족합니다.
fizzybear

1
@ fizzybear는 분명히 반대의 접근법을 취합니다. 포인터는 내 친구이며 90 년대 초반부터 "null"/ "nil"포인터 문제가 없었습니다. 나는 nullable / nilable을 모두 없애 버릴 수 있기를 바랍니다. 그러나 실제 답변은 답변의 첫 6 줄에 있습니다. 그러나 당신의 질문에 대해 : 내가 할 일이 아니라 (비평이 없음) 그래서 나는 모른다.
윌리엄 Cerniuk
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.