null/nil以及Nullability Annotations

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wf96390/article/details/52073845

参考:
http://nshipster.cn/nil/
https://developer.apple.com/swift/blog/?id=25
http://blog.sunnyxx.com/2015/06/12/objc-new-features-in-2015/
http://www.cocoachina.com/ios/20150603/11989.html

null/nil

NULL

C用0来作为不存在的原始值,而NULL作为指针(这在指针环境中相当于0)。

nil/Nil

Objective-C在C的表达不存在的基础上增加了nil。nil是一个指向不存在的对象指针。虽然它在语义上与NULL不同,但它们在技术上是相等的。
另外,在Foundation/NSObjCRuntime.h中,Nil被定义为指向零的类指针。
刚被分配的NSObject对象所有的指向其他对象的指针都从nil开始。nil最显著的行为是,它虽然为零,仍然可以有消息发送给它。
在其他的语言中,比如C++,这样做会使你的程序崩溃,但在Objective-C中,在nil上调用方法返回一个零值。这大大的简化了表达,因为它避免了在使用nil之前对它的检查:

// 举个例子,这个表达...
if (name != nil && [name isEqualToString:@"Steve"]) { ... }

// …可以被简化为:
if ([name isEqualToString:@"steve"]) { ... }

在Objective-C中调用方法时,实际调用objc_msgSend,会处理nil的情况,官方文档中是这样写的
Send message to nil
If you expect a return value from a message sent to nil, the return value will be nil for object return types, 0 for numeric types, and NO for BOOL types. Returned structures have all members initialized to zero.

NSNull

在框架层面,Foundation定义了NSNull,即一个类方法+null,它返回一个单独的NSNull对象。NSNull与nil以及NULL不同,因为它是一个实际的对象,而不是一个零值。
NSNull在Foundation和其它框架中被广泛的使用,以解决如NSArray和NSDictionary之类的集合不能有nil值的缺陷。你可以将NSNull理解为有效的将NULL或者nil值封装boxing,以达到在集合中使用它们的目的:

NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary];
mutableDictionary[@"someKey"] = [NSNull null]; // Sets value of NSNull singleton for `someKey`
NSLog(@"Keys: %@", [mutableDictionary allKeys]); // @[@"someKey"]

Nullability Annotations and Objective-C

Nullability 并不算新特性了,从上一个版本的 llvm 6.1 (Xcode 6.3) 就已经支持。这个简版的 Optional ,没有 Swift 中 ? 和 ! 语法糖的支持,在 Objective-C 中使用,
可以使用 _Nullable 和 _Nonnull 在大部分可以使用const关键字的地方

- (AAPLListItem * _Nullable)itemWithName:(NSString * _Nonnull)name;
@property (copy, readonly) NSArray * _Nonnull allItems;
也可以在圆括号开始的地方使用 nullable 和 nonnull
- (nullable AAPLListItem *)itemWithName:(nonnull NSString *)name;
- (NSInteger)indexOfItem:(nonnull AAPLListItem *)item;

或者在属性中可以使用

@property (copy, nullable) NSString *name;
@property (copy, readonly, nonnull) NSArray *allItems;

从 iOS9 SDK 中可以发现,头文件中所有 API 都已经增加了 Nullability 相关修饰符。
This feature was first released in Xcode 6.3 with the keywords __nullable and __nonnull. Due to potential conflicts with third-party libraries, we’ve changed them in Xcode 7 to the _Nullable and _Nonnull you see here. However, for compatibility with Xcode 6.3 we’ve predefined macros __nullable and __nonnull to expand to the new names.

The non-underscored forms are nicer than the underscored ones, but you’d still need to apply them to every type in your header. To make that job easier and to make your headers clearer, you’ll want to use audited regions.

Audited Regions

接口中 nullable 的是少数,所以为了防止写一大堆 nonnull,Foundation 还提供了一对宏,包在里面的对象默认加 nonnull 修饰符,只需要把 nullable 的指出来就行

NS_ASSUME_NONNULL_BEGIN
@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
 - (nullable AAPLListItem *)itemWithName:(NSString *)name;
 - (NSInteger)indexOfItem:(AAPLListItem *)item;

@property (copy, nullable) NSString *name;
@property (copy, readonly) NSArray *allItems;
// ...
@end
NS_ASSUME_NONNULL_END

// --------------

self.list.name = nil;   // okay

AAPLListItem *matchingItem = [self.list itemWithName:nil];  // warning!

不过,为了安全起见,苹果还制定了几条规则:
1 typedef定义的类型的nullability特性通常依赖于上下文,即使是在Audited Regions中,也不能假定它为nonnull。
2 复杂的指针类型(如id )必须显示去指定是nonnull还是nullable。例如,指定一个指向nullable对象的nonnull指针,可以使用”__nullable id __nonnull”。
3 我们经常使用的NSError **通常是被假定为一个指向nullable NSError对象的nullable指针。

扫描二维码关注公众号,回复: 5398725 查看本文章

兼容性

因为Nullability Annotations是Xcode 6.3新加入的,所以我们需要考虑之前的老代码。实际上,苹果已以帮我们处理好了这种兼容问题,我们可以安全地使用它们:
- 老代码仍然能正常工作,即使对nonnull对象使用了nil也没有问题,给nil发送消息也可以。
- 老代码在需要和swift混编时,在新的swift编译器下会给出一个警告。
- nonnull不会影响性能。事实上,我们仍然可以在运行时去判断我们的对象是否为nil。

事实上,我们可以将nonnull/nullable与我们的断言和异常一起看待,其需要处理的问题都是同一个:违反约定是一个程序员的错误。特别是,返回值是我们可控的东西,如果返回值是nonnull的,则我们不应该返回nil,除非是为了向后兼容。

+ (nullable instancetype)URLWithString:(NSString *)URLString;

NSURL 的这个 API 前面加了 nullable 后,更加显式的指出了这个接口可能因为 URLString 的格式错误而创建失败,使用时自然而然的就考虑到了判空处理。
不仅是属性和方法中的对象,对于局部的对象、甚至 c 指针都可以用带双下划线的修饰符。所有可以使用const的地方都可以使用。

猜你喜欢

转载自blog.csdn.net/wf96390/article/details/52073845