Objective-C 中 Nullability 和 Swift 中的 ? or !

Nullability Annotations

就是这两个东西_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

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

你可能感兴趣的:(objective-c,swift,Swift可选值,nonnull修饰,Nullabilit,基础知识)