之所以會(huì)有這篇文章, 是因?yàn)樽罱黃wift5出來(lái)后, 公司項(xiàng)目有意向往混編的方向走, 而純Objc的老項(xiàng)目嘛...emmmm, 直接用的話轉(zhuǎn)成Swift后真的一言難盡, 所以為了讓公司其他人更好的改寫(xiě)用到的頭文件, 就整理了一下難用的Nullability到底怎么用會(huì)比較方便, 順便看了一下互相轉(zhuǎn)換的宏哪個(gè)還用得上
開(kāi)始改造
首先是Nullability, 先簡(jiǎn)單粗暴的用:
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
把整個(gè)頭文件包起來(lái)(import那幾行不需要, 一般不包這幾行)
這樣整個(gè)頭文件所有類(lèi)型都默認(rèn)是非optional的
然后再把需要變成可選的單獨(dú)加上Nullability關(guān)鍵字即可:
而考慮到各種問(wèn)題, 比如官方一般用nullable
, 而有些地方只能用__nullable
, 所以最簡(jiǎn)單總結(jié)起來(lái)就是:
- 無(wú)腦類(lèi)型后綴
__nullable
就好(block是在^后綴) -
_Nullable
就不要碰了, 混在一起容易亂 - 至于property里跟不跟官方用
nullable
? 推薦寫(xiě)成CodeSnippet自動(dòng)生成然后不要碰了(其實(shí)也就strong和copy需要nullable):
@property (<#nullable, #>nonatomic, strong) <#Class#> *<#name#>;
@property (<#nullable, #>nonatomic, copy) NSString *<#name#>;
或者跟著官方的做法:
- 一般情況下無(wú)腦前綴
nullable
- 遇到block相關(guān)的就類(lèi)型后綴
__nullable
加以區(qū)分,也比較好記(block是在^后綴) -
_Nullable
就不要碰了
不管選哪個(gè), 重點(diǎn)其實(shí)是整個(gè)項(xiàng)目保持一致性才是最重要的
精致分割線
如果上面的總結(jié)不能幫到你, 具體解釋就是:
-
__nullable
/_Nullable
是編譯器參數(shù),需要放到類(lèi)型后面,也就是NSString *__nullable
這樣
ps: OC里泛型是不能__nullable
的, 我一時(shí)沒(méi)想通傻試了好久, emmm...反應(yīng)過(guò)來(lái)的時(shí)候差點(diǎn)笑死
-
nullable
是屬性, 可以和strong
/readonly
一樣放到property的括號(hào)里, 或者作為參數(shù)時(shí)和__weak
一樣前綴到變量類(lèi)型前面:
para:(nullable NSString *)name
根據(jù)上面的規(guī)則就能衍變出:
property有兩種寫(xiě)法
為了方便說(shuō)明, copy/readonly這些稱(chēng)為property的屬性
@property (copy) NSString *__nullable name;
@property (nullable, copy) NSString * name; // 本質(zhì)還是前綴, 但property的屬性需要寫(xiě)到括號(hào)里, 雖然這是官方寫(xiě)法, 但為了不要搞混最好不要記這個(gè), 屬性用CodeSnippet生成就好
ps1: property還有一種nullable
屬性null_resettable
字面意思就是setter可以傳空, getter不能返回為空,編譯器改寫(xiě)成Swift時(shí)會(huì)用!來(lái)表示, 如UIViewController.view就是null_resettable的:
@property(null_resettable, nonatomic,strong) UIView *view;//這里復(fù)制過(guò)來(lái)就這樣的,蘋(píng)果少打了一個(gè)空格
ps2: weak不能用nonnull
方法的返回值和參數(shù)也各自有兩種寫(xiě)法:
- (NSString *__nullable)nameForItem:(NSString *__nullable)item;
- (nullable NSString *)nameForItem:(nullable NSString *)item;// 官方也是這種寫(xiě)法, 還是那句話, 不要記這個(gè)
最麻煩的是block
block作為property
它本身是不是optional需要在^后綴__nullable
, 或者跟上面的property一樣寫(xiě)成屬性到括號(hào)里
@property (copy) void (^__nullable aBlock)();
@property (nullable, copy) void (^ aBlock)();
block做參數(shù)也是兩種寫(xiě)法:
它本身是不是optioanl可以在^后綴__nullable
, 前綴nullable
, 但返回值和參數(shù)只能后綴__nullable
- (void)needABlock:(id __nullable (^__nullable)(id __nullable para))aBlock;
- (void)needABlock:(nullable id __nullable (^)(id __nullable para))aBlock;
返回值前綴nullable
會(huì)沖突這個(gè)很容易理解了, 所以反過(guò)來(lái)想, 大概是為了和返回值保持一致, 所以參數(shù)也只能后綴__nullable
了吧...
ps: 如果nulable的block是最后一個(gè)參數(shù), Swift會(huì)自動(dòng)轉(zhuǎn)換成帶默認(rèn)值nil
open func needABlock(_ aBlock: ((Any?) -> Void)? = nil)
而普通類(lèi)型的nullable變量則不會(huì)
最后是block的typedef
基本規(guī)則跟做參數(shù)是一樣的, 但是定義這個(gè)type是不是optional跟做參數(shù)不同, 只能在^后綴__nullable
(所以無(wú)腦類(lèi)型后綴__nullable
就好了):
typedef id __nullable (^__nullable ABlock)(id __nullable para);
還有一種 null_unspecified
代表不確定是不是為空, 這個(gè)一般用不上, 總之大概跟nullable
的用法差不多, 同樣有編譯器參數(shù)__null_unspecified
/_Null_unspecified
當(dāng)既沒(méi)有用ASSUME_NONNULL把頭文件包起來(lái), 也沒(méi)有逐個(gè)添加Nullability時(shí), 編譯器就會(huì)默認(rèn)用這個(gè)作為變量的Nullability
如果真的不能確定到底會(huì)不會(huì)為空(以后可能會(huì)為空), 可以用這個(gè), 編譯器改寫(xiě)成Swift時(shí)會(huì)用!來(lái)表示(和null_resettable
一樣, 區(qū)別是OC里的警告不同), 如:
@property (null_unspecified) id name;
會(huì)被改寫(xiě)成:
open var name: Any!
接著還有一個(gè)用于命名的關(guān)鍵字是
NS_SWIFT_NAME(<#swift專(zhuān)用名#>)
可以用于任意內(nèi)容, 包括類(lèi)名, 屬性名, 枚舉:
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:));