就是这两个东西_Nullable and _Nonnull
1.在Swift中对于option和non-option有着很明显的区分,例如NSView!(NSView对于Swift而已也是non-option的)和NSView?,但是OC中只有一个写法就是NSView ,即可以表示option,也可以表示non-option的,那么问题来了,当OC和Swift混编的时候就会出问题,Swift编译器不能确定NSView 是optional还是non-optional,swfit编译器默认会认为是non-optional的,从而进行强制解析,NSView!
2.Xcode 6之后的SDK版本跌到,引申出了_Nullable and _Nonnull
顾名思义,_Nullable修饰的属性,值可以是NULL或者nil,那么_Nonnull就是相反,这就是规则,如果你违反了,那么编译器就会警告
@interface AAPLList : NSObject
// ...
- (AAPLListItem * _Nullable)itemWithName:(NSString * _Nonnull)name;
@property (copy, readonly) NSArray * _Nonnull allItems;
// ...
@end
// --------------
[self.list itemWithName:nil]; // warning
// 方法里面传入的参数要求是_Nonnull的,那么传入nil就警告
一般来讲,所有能使用C中const关键字的地方都可以使用_Nullable and _Nonnull
当然,他们必须都是指针类型,而且只要你的类型是简单的对象或者Block指针类型,那么就可以去掉下划线,直接用nullable and nonnull
- (nullable AAPLListItem *)itemWithName:(nonnull NSString *)name;
- (NSInteger)indexOfItem:(nonnull AAPLListItem *)item;
同样对于属性,也可以用非下划线的关键字修饰
@property (copy, nullable) NSString *name;
@property (copy, readonly, nonnull) NSArray *allItems;
很显然没有下划线的格式更好看一点,但是缺点就是你还是需要在每个用到的地方进行申明,为了不出现重复的东西,我们将使用audited regions
NS_ASSUME_NONNULL_BEGIN 和 NS_ASSUME_NONNULL_END
为了减少繁琐的工作,apple提供了两个宏,提供给开发者,把需要的区域包裹起来,这个区域之内,默认都是nonnull修饰,那么如果很多方法和属性中,你需要某个值可以为nil或者NULL,那么你单独给它用nullable修饰,覆盖默认的nonnull即可
NS_ASSUME_NONNULL_BEGIN
@interface AAPLList : NSObject <NSCoding, NSCopying>
// ...
// 返回值可以为null,调用者需要自己兼容判断
- (nullable AAPLListItem *)itemWithName:(NSString *)name;
- (NSInteger)indexOfItem:(AAPLListItem *)item;
// 属性可以为nil
@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!
以下就是apple头文件里面的部分截取,用宏定义包裹,内部如果是nullable,就单独修饰
NS_ASSUME_NONNULL_BEGIN
+ (nullable UIImage *)imageNamed:(NSString *)name; // load from main bundle
#if __has_include()
+ (nullable UIImage *)imageNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection NS_AVAILABLE_IOS(8_0);
apple有如下三个特例规则
1.typedef指定的nullability特性都是依赖于上下文,即使在Audited Regions区域内,也不能默认为nonnull
2.复杂的指针类型(如id *)必须显示去指定是nullable 还是 nonnull,例如指定一个指向nullable对象的nonnull指针,可以使用_Nullable id * _Nonnull
。
3. NSError **通常是被假定为一个指向nullable NSError对象的nullable指针。
其实,现在理解Swift里面的!和?就可以了,而且如果什么都没有,默认就是!(nonnull的)
没有用宏定义的时候,这个时候,就需要自己针对每个属性和方法的参数进行!和?修饰
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 }
// ...
}
有用宏定义的时候,这个时候,默认就是!类型的,如果你有可以支持nil或者NULL的属性参数时,针对需要的属性覆盖之前的!,手动加上?即可
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 }
// ...
}
apple文档
NShipster