[objective-c] Objective-C에서 nullable, __nullable 및 _Nullable의 차이점

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.

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

그래서 내 질문은 :

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



답변

로부터 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

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


답변

에서 스위프트 블로그 :

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


답변

나는 이 기사를 정말로 좋아 했기 때문에 저자가 쓴 것을 보여주고있다 :
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;


답변

매우 편리합니다

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 );


답변