您的位置:首页 > 移动开发 > Objective-C

Objective-C关键字__Nullable和__Nonnull

2015-12-10 15:53 671 查看
Xcode 6.3中有一项新特性,`nullability`标记,这为 OC 提供了类似 Swift 中的 Optional 的类型
为了让更多开发者从 OC 向 Swift 过渡,OC 和 Swift 混编已经变得尤为重要,就像是OC通向Swift的一座桥梁,
通过引入泛型使得 OC 和 Swift 之间更加安全清楚的理解和共享包含特定元素的集合

The Core: _Nullable and _Nonnull

关于 _Nullable 和 _Nonnull ,官方文档有这么一句话

At the core of this feature we have two new type annotations: _Nullable and _Nonnull.

As you might expect, a _Nullable pointer may have a NULL or nil value, while a _Nonnull one should not.

The compiler will tell you if you try to break the rules.


大概意思就是,我们又添加了两种类型,
_Nullable
可以为
NULL
或者
nil
,反之
_Nonnull
绝对不能为空,否则编译器就会出现警告

@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
- (AAPLListItem * _Nullable)itemWithName:(NSString * _Nonnull)name;
@property (copy, readonly) NSArray * _Nonnull allItems;
// ...
@end
// --------------
[self.list itemWithName:nil]; // warning!


事实上你可以在任何可以使用C语言关键字
const
的地方使用
_Nullable
_Nonnull


当然
_Nullable
_Nonnull
一般用于指针类型,在一般情况下,有一个更好的方法去实现这种标记的功能

。即在方法申明的时候可以使用
non-underscored
形式,
nullable
nonnull
,直接写在类型前面(类型必须是一个对象或者block指针)。

- (nullable AAPLListItem *)itemWithName:(nonnull NSString *)name;
- (NSInteger)indexOfItem:(nonnull AAPLListItem *)item;


在属性中,同样可以使用
non-underscored
形式将这样的关键字放到属性列表中

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


non-underscored
方式比
underscored
方式有很明显的优势,但是即便如此,你仍然需要将它们写在每一种类型中去

为了更加高效的工作和代码的简洁,我们需要用到一种苹果称之为
audited regions
的方法

什么是
Audited Regions

为了方便采用新的方式,你可以在
Objective-C
头文件标记一块区域作为
audited for nullability
,在这块区域内,

任何简单指针类型都将被标注为
nonnull
类型,这样一来,如果有一大段代码需要加注
nonnull
,就比前面那种繁琐的方式简单多了。

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
类型不会被当做
nonnull
处理,即使在
audited regions


2.很多复杂的指针,譬如
id *
必须明确标注,例如要制定一个非空的指针可以被空对象引用,要这样写
_Nullable id * _Nonnull


3.特定类型
NSError **
经常通过方法的参数返回错误以致它总是被假定为一个可以被空指针引用的空指针

兼容性

如果已经存在的
Objective-C
代码被重写会怎样?单单以这种方式改变类型安全么?没错,是安全的。

现有的编译代码会在你的框架上继续跑,即
ABI
不会变,就是说现有的代码在运行时出现
nil
不会报错

当你使用新的
Swift
编译器,使用不安全的方式在编译时现有的源代码在你的框架可能会有额外的警告

nonnull
不影响优化,尤其是在运行时你仍然能检查标注为
nonnull
的参数是否是nil,向后兼容是很重要的。

这个功能最早被发布在
Xcode 6.3
上,关键字是
__nullable
__nonnull
,由于一些三方库潜在的冲突,

Xcode 7
上将关键字改为这里提到的
_Nullable
_Nonnull
,为了兼容
Xcode 6.3
,苹果定义了

__nullable
__nonnull


回到
Swift

现在我们已经
nullability
标注添加到我们的
Objective-C
头文件中,来看看怎么在
Swift
中使用它

在没有添加标注前的
Objective-C
转换成
Swift
应该是这样的

class AAPLList : NSObject, NSCoding, NSCopying {
// ...
func itemWithName(name: String!) -> AAPLListItem!
func indexOfItem(item: AAPLListItem!) -> Int

@NSCopying var name: String! { get set }
@NSCopying var allItems: [AnyObject]! { get }
// ...
}


添加之后是这样的

class AAPLList : NSObject, NSCoding, NSCopying {
// ...
func itemWithName(name: String) -> AAPLListItem?
func indexOfItem(item: AAPLListItem) -> Int

@NSCopying var name: String? { get set }
@NSCopying var allItems: [AnyObject] { get }
// ...
}


这样一来,
Swift
代码看起来就简洁多了,虽然是一个微妙的变化,但这会让你的框架更完美
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息