
投稿文章,作者:龐海礁(博客)
ReactNative是Facebook開源的一種實現移動跨平台開發的解決方案,目前在業界得到廣泛應用,這裡有非常詳細的中文使用指南。本文主要分享RN源碼中一些值得大家學習或者借鑒的代碼或者編寫技巧等,供大家學習參考。
整個RN庫包含10多個工程,有興趣的童鞋可以下載源碼查看具體細節,在此不再展開。

宏定義巧用
整個ReactNative源碼工程中用到了大量的宏定義,包括RCT_EXTERN、RCT_NOT_IMPLEMENTED、RCT_EXPORT_METHOD以及RCT_EXPORT_MODULE等申明宏或者功能宏。通過宏定義的方式,可以非常方便嵌入功能代碼或者邏輯實現,重用代碼的同時又保持了代碼的整潔性
比如,ProtocolKit工程中,作者通過宏定義@defs將Protocol接口巧妙的實現在.h文件中,代碼簡介明了,又不失功能完整性。當然,RN工程中,RCT_NOT_IMPLEMENTED宏也有相似作用,實際項目中各位也可以嘗試通過宏定義實現一些常用功能模塊
關於iOS宏定義的文章有很多,在此推薦兩篇非常不錯的文章:RAC中必須要知道的宏、iOS宏的使用和技巧。
環境變量
iOS開發中,各位對#ifdef DEBUG應該非常熟悉,通過判斷該條件,可以區別當前運行環境是Debug環境還是Release環境。比如Release環境下通過重定義NSLog以屏蔽所有日志輸出
#ifdef DEBUG
#define NSLog(...) NSLog(__VA_ARGS__)
#else
#define NSLog(...) {}
#endif進一步,是否可以考慮只在聯機調試環境下輸出日志?此時就涉及聯機調試環境的判斷,環境變量正好可以解決該問題

Xcode可以在不同環境下自定義環境變量Environment Variables,通過在運行環境Run中自定義變量CI_USE_PACKAGER,此時便可在項目代碼中通過getenv()函數判斷當前運行環境
if (getenv("CI_USE_PACKAGER")) {
// to do...
}被忽略的硬鍵盤
相較於軟鍵盤文字符號的輸入,對於APP來說,硬鍵盤的應用開發似乎很容易被忽視,畢竟,通常情況下,硬鍵盤輸入只會出現在模擬器環境下。
iOS7以後,系統定義有硬鍵盤響應交互類UIKeyCommand,通過UIKeyCommand,APP能夠監聽硬鍵盤的特定輸入響應,比如Command+D等,當然,前提是APP需要首先監聽該輸入命令。
UIKeyCommand的使用非常簡單,當需要在特定場景觸發某一事件,但又不想影響界面顯示的時候,不妨試試UIKeyCommand,具體使用可以看看這篇文章。
_cmd
iOS官方文檔中,_cmd表示當前方法的selector,你可以通過下面代碼打印輸出當前函數名
NSLog(@"Current method: %@", NSStringFromSelector(_cmd));
當然,實際項目中,你也可以這樣使用
NSNumber *rootTag = objc_getAssociatedObject(self, _cmd) ?: @1; objc_setAssociatedObject(self, _cmd, @(rootTag.integerValue + 10), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
瞧,是不是有點意思!
kCFNull
相對於nil NSNull而言,kCFNull筆者接觸較少,kCFNull可以理解為NSNull單例對象
id null1 = (id)kCFNull; id null2 = [NSNull null];
打印地址
null1=(NSNull *)0x10426eaf0 null2=(NSNull *)0x10426eaf0
從上面測試結果可以看出它們其實指向同一地址, 可以簡單理解為 kCFNull === [NSNull null]
文本陰影NSShadow
APP開發中,程序猿可能經常需要在圖片或視頻上顯示文字,由於背景顏色跟文字顏色相近,導致文字看不清,比如時下火熱的直播彈幕顯示,為了確保文字顯示清晰,開發者一般會配上陰影或者文字描邊
給文本添加陰影描邊,系統提供有NSShadow類,可以這樣使用
NSShadow *shadow = [NSShadow new];
shadow.shadowOffset = CGSizeZero;
shadow.shadowBlurRadius = 5.0f;
shadow.shadowColor = [UIColor colorWithWhite:0.0f alpha:0.3f];
NSAttributedString *attString = [[NSAttributedString alloc] initWithString:@"www.olinone.com"
attributes:@{NSShadowAttributeName: shadow,
NSForegroundColorAttributeName: [UIColor whiteColor]}];
lbl.attributedText = attString;實際效果是這樣的,shadowBlurRadius值越小,文本描邊越清晰

主線程判斷
判斷當前執行線程是否為主線程的方法有很多,比如
[NSThread isMainThread] pthread_main_np
在RN中,它是這樣的
BOOL RCTIsMainQueue() {
static void *mainQueueKey = &mainQueueKey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueKey, NULL);
});
return dispatch_get_specific(mainQueueKey) == mainQueueKey;
}當然,由於無法查看NSThread內部實現機制,暫時無法了解孰優孰劣,不過,[NSThread isMainThread]貌似足矣!
volatile不簡單
在百科中,是這樣描述它的:就像大家更熟悉的const一樣,volatile是一個類型修飾符,它是被設計用來修飾被不同線程訪問和修改的變量。作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值
簡單說,被volatile修飾的變量是多線程安全的,其次,不會因為編譯器優化導致讀值出錯。關於編譯器編譯優化可以看看這篇文章。
iOS開發中確保多線程安全的方法有很多,原子操作、線程鎖、單線程執行等等,本人也寫過相關文章iOS開發多線程同步。
在RN中,通過volatile修飾符,巧妙實現了多線程取消操作
__block volatile uint32_t cancelled = 0;
if (!cancelled) {
// to do...
}
OSAtomicOr32Barrier(1, &cancelled);通過原子性操作訪問被volatile修飾的cancelled對象即可保障函數只執行一次。想想大家熟悉的單例dispatch_once_t,現在讓你設計單例對象,你又會如何設計了?
+ (instancetype)sharedInstance {
static RCTWebSocketManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self new];
});
return sharedInstance;
}結構體Struct
說起Struct,不知各位對它印象如何?大學C課本中學過?NSObject類class原型貌似有講?
struct iOSDev {
NSString *nickName;
};OC中一個簡單的結構體,在Swift中,Struct也可以這樣寫
struct iOSDev {
var nickName : String
func getBusinessCard() -> String {
return "(nickName),幽默的iOS開發者!"
}};
let iOSOlinone = iOSDev(nickName: "olinone")
print(iOSOlinone.getBusinessCard())getBusinessCard為結構體函數,是不是感覺很方便!其實OC中也可以這樣寫
struct iOSDev {
NSString *nickName;
NSString *getBusinessCard() {
return [NSString stringWithFormat:@"%@,幽默的iOS開發者!", nickName];
}};
iOSDev iosDev = iOSDev{@"olinone"};
NSLog(@"%@", iosDev.getBusinessCard());當然,為Struct添加函數並不是C語言特性,而是C++特性,因此,為了編譯通過,你需要將.m文件修改成.mm文件。
Struct有其使用的特殊場景,相較於Class,合理的使用Struct可以使代碼更加整潔。同時,為了適應Swift中Struct強大特性,可以試著在OC項目中嘗試Struct。