iOS项目头文件改造

之所以会有这篇文章, 是因为最近Swift5出来后, 公司项目有意向往混编的方向走, 而纯Objc的老项目嘛...emmmm, 直接用的话转成Swift后真的一言难尽, 所以为了让公司其他人更好的改写用到的头文件, 就整理了一下难用的Nullability到底怎么用会比较方便, 顺便看了一下互相转换的宏哪个还用得上

开始改造

首先是Nullability, 先简单粗暴的用:

NS_ASSUME_NONNULL_BEGIN
    
NS_ASSUME_NONNULL_END

把整个头文件包起来(import那几行不需要, 一般不包这几行)

这样整个头文件所有类型都默认是非optional的

然后再把需要变成可选的单独加上Nullability关键字即可:

而考虑到各种问题, 比如官方一般用nullable, 而有些地方只能用__nullable, 所以最简单总结起来就是:
  1. 无脑类型后缀__nullable就好(block是在^后缀)
  2. _Nullable就不要碰了, 混在一起容易乱
  3. 至于property里跟不跟官方用nullable? 推荐写成CodeSnippet自动生成然后不要碰了(其实也就strong和copy需要nullable):
@property (<#nullable, #>nonatomic, strong) <#Class#> *<#name#>;
@property (<#nullable, #>nonatomic, copy) NSString *<#name#>;

或者跟着官方的做法:

  1. 一般情况下无脑前缀nullable
  2. 遇到block相关的就类型后缀__nullable加以区分,也比较好记(block是在^后缀)
  3. _Nullable就不要碰了

不管选哪个, 重点其实是整个项目保持一致性才是最重要的


精致分割线


如果上面的总结不能帮到你, 具体解释就是:

  1. __nullable/_Nullable是编译器参数,需要放到类型后面,也就是NSString *__nullable这样

ps: OC里泛型是不能__nullable的, 我一时没想通傻试了好久, emmm...反应过来的时候差点笑死

  1. nullable是属性, 可以和strong/readonly一样放到property的括号里, 或者作为参数时和__weak一样前缀到变量类型前面:
para:(nullable NSString *)name

根据上面的规则就能衍变出:

property有两种写法

为了方便说明, copy/readonly这些称为property的属性

@property (copy) NSString *__nullable name;

@property (nullable, copy) NSString * name; // 本质还是前缀, 但property的属性需要写到括号里, 虽然这是官方写法, 但为了不要搞混最好不要记这个, 属性用CodeSnippet生成就好

ps1: property还有一种nullable属性null_resettable

字面意思就是setter可以传空, getter不能返回为空,编译器改写成Swift时会用!来表示, 如UIViewController.view就是null_resettable的:
@property(null_resettable, nonatomic,strong) UIView *view;//这里复制过来就这样的,苹果少打了一个空格

ps2: weak不能用nonnull

方法的返回值和参数也各自有两种写法:
- (NSString *__nullable)nameForItem:(NSString *__nullable)item;

- (nullable NSString *)nameForItem:(nullable NSString *)item;// 官方也是这种写法, 还是那句话, 不要记这个

最麻烦的是block

block作为property

它本身是不是optional需要在^后缀__nullable, 或者跟上面的property一样写成属性到括号里

@property (copy) void (^__nullable aBlock)();

@property (nullable, copy) void (^ aBlock)();
block做参数也是两种写法:

它本身是不是optioanl可以在^后缀__nullable, 前缀nullable, 但返回值和参数只能后缀__nullable

- (void)needABlock:(id __nullable (^__nullable)(id __nullable para))aBlock;
- (void)needABlock:(nullable id __nullable (^)(id __nullable para))aBlock;

返回值前缀nullable会冲突这个很容易理解了, 所以反过来想, 大概是为了和返回值保持一致, 所以参数也只能后缀__nullable了吧...

ps: 如果nulable的block是最后一个参数, Swift会自动转换成带默认值nil

open func needABlock(_ aBlock: ((Any?) -> Void)? = nil)

而普通类型的nullable变量则不会

最后是block的typedef

基本规则跟做参数是一样的, 但是定义这个type是不是optional跟做参数不同, 只能在^后缀__nullable(所以无脑类型后缀__nullable就好了):

typedef id __nullable (^__nullable ABlock)(id __nullable para);
还有一种 null_unspecified

代表不确定是不是为空, 这个一般用不上, 总之大概跟nullable的用法差不多, 同样有编译器参数__null_unspecified/_Null_unspecified

当既没有用ASSUME_NONNULL把头文件包起来, 也没有逐个添加Nullability时, 编译器就会默认用这个作为变量的Nullability

如果真的不能确定到底会不会为空(以后可能会为空), 可以用这个, 编译器改写成Swift时会用!来表示(和null_resettable一样, 区别是OC里的警告不同), 如:

@property (null_unspecified) id name;

会被改写成:

open var name: Any!

接着还有一个用于命名的关键字是
NS_SWIFT_NAME(<#swift专用名#>)
可以用于任意内容, 包括类名, 属性名, 枚举:

NS_SWIFT_NAME(VoiceFilter)
@interface ABVoiceFilter : NSObject
@end

typedef NS_ENUM(NSUInteger, AType) {
    ATypeNone NS_SWIFT_NAME(NoneOne),
    ATypeOther NS_SWIFT_NAME(OtherPeople) ,
};

- (void)handleConnectItem:(id)connectionItem withParser:(id)parser NS_SWIFT_NAME(handle(item:parser:));

你可能感兴趣的:(iOS项目头文件改造)