ReactNative源码笔记——你知道几条?

nzxn8335 8年前
   <p><a href="/misc/goto?guid=4958869360543124738" rel="nofollow,noindex">ReactNative</a> 是非死book开源的一种实现移动跨平台开发的解决方案,目前在业界得到广泛应用, <a href="/misc/goto?guid=4959655646655543307" rel="nofollow,noindex">这里</a> 有非常详细的中文使用指南。本文主要分享RN源码中一些值得大家学习或者借鉴的代码或者编写技巧等,供大家学习参考</p>    <p>整个RN库包含10多个工程,有兴趣的童鞋可以下载 <a href="/misc/goto?guid=4958869360543124738" rel="nofollow,noindex">源码</a> 查看具体细节,在此不再展开</p>    <p><img src="https://simg.open-open.com/show/f32144c0fcfe1621cb82cc8ffd19bbb7.png"></p>    <h2>宏定义巧用</h2>    <p>整个ReactNative源码工程中用到了大量的宏定义,包括RCT_EXTERN、RCT_NOT_IMPLEMENTED、RCT_EXPORT_METHOD以及RCT_EXPORT_MODULE等申明宏或者功能宏。通过宏定义的方式,可以非常方便嵌入功能代码或者逻辑实现,重用代码的同时又保持了代码的整洁性</p>    <p>比如, <a href="/misc/goto?guid=4959675399490453743" rel="nofollow,noindex">ProtocolKit</a> 工程中,作者通过宏定义@defs将Protocol接口巧妙的实现在.h文件中,代码简介明了,又不失功能完整性。当然,RN工程中,RCT_NOT_IMPLEMENTED宏也有相似作用,实际项目中各位也可以尝试通过宏定义实现一些常用功能模块</p>    <p>关于iOS宏定义的文章有很多,在此推荐两篇非常不错的文章: <a href="/misc/goto?guid=4959675399609781032" rel="nofollow,noindex">RAC中必须要知道的宏</a> 、 <a href="/misc/goto?guid=4959675399713722751" rel="nofollow,noindex">ios宏的使用和技巧</a></p>    <h2>环境变量</h2>    <p>iOS开发中,各位对#ifdef DEBUG应该非常熟悉,通过判断该条件,可以区别当前运行环境是Debug环境还是Release环境。比如Release环境下通过重定义NSLog以屏蔽所有日志输出</p>    <pre>  <code class="language-objectivec">#ifdef DEBUG    #define NSLog(...) NSLog(__VA_ARGS__)    #else    #define NSLog(...) {}    #endif  </code></pre>    <p>进一步,是否可以考虑只在联机调试环境下输出日志?此时就涉及联机调试环境的判断,环境变量正好可以解决该问题</p>    <p><img src="https://simg.open-open.com/show/31b48c19baa31cc94760cb64d070d349.png"></p>    <p>Xcode可以在不同环境下自定义环境变量Environment Variables,通过在运行环境Run中自定义变量CI_USE_PACKAGER,此时便可在项目代码中通过getenv()函数判断当前运行环境</p>    <pre>  <code class="language-objectivec">if (getenv("CI_USE_PACKAGER")) {    // to do...  }  </code></pre>    <h2>被忽略的硬键盘</h2>    <p>相较于软键盘文字符号的输入,对于APP来说,硬键盘的应用开发似乎很容易被忽视,毕竟,通常情况下,硬键盘输入只会出现在模拟器环境下</p>    <p>iOS7以后,系统定义有硬键盘响应交互类UIKeyCommand,通过UIKeyCommand,APP能够监听硬键盘的特定输入响应,比如Command+D等,当然,前提是APP需要首先监听该输入命令</p>    <p>UIKeyCommand的使用非常简单,当需要在特定场景触发某一事件,但又不想影响界面显示的时候,不妨试试UIKeyCommand,具体使用可以看看这篇 <a href="/misc/goto?guid=4958969710366005408" rel="nofollow,noindex">文章</a></p>    <h2>_cmd</h2>    <p>iOS官方文档中,_cmd表示当前方法的selector,你可以通过下面代码打印输出当前函数名</p>    <pre>  <code class="language-objectivec">NSLog(@"Current method: %@", NSStringFromSelector(_cmd));  </code></pre>    <p>当然,实际项目中,你也可以这样使用</p>    <pre>  <code class="language-objectivec">NSNumber *rootTag = objc_getAssociatedObject(self, _cmd) ?: @1;  objc_setAssociatedObject(self, _cmd, @(rootTag.integerValue + 10), OBJC_ASSOCIATION_RETAIN_NONATOMIC);  </code></pre>    <p>瞧,是不是有点意思!</p>    <h2>kCFNull</h2>    <p>相对于nil NSNull而言,kCFNull笔者接触较少,kCFNull可以理解为NSNull单例对象</p>    <pre>  <code class="language-objectivec">id null1 = (id)kCFNull;  id null2 = [NSNull null];  </code></pre>    <p>打印地址</p>    <pre>  <code class="language-objectivec">null1=(NSNull *)0x10426eaf0  null2=(NSNull *)0x10426eaf0  </code></pre>    <p>从上面测试结果可以看出它们其实指向同一地址, 可以简单理解为 kCFNull === [NSNull null]</p>    <h2>文本阴影NSShadow</h2>    <p>APP开发中,程序猿可能经常需要在图片或视频上显示文字,由于背景颜色跟文字颜色相近,导致文字看不清,比如时下火热的直播弹幕显示,为了确保文字显示清晰,开发者一般会配上阴影或者文字描边</p>    <p>给文本添加阴影描边,系统提供有NSShadow类,可以这样使用</p>    <pre>  <code class="language-objectivec">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;  </code></pre>    <p>实际效果是这样的,shadowBlurRadius值越小,文本描边越清晰</p>    <p><img src="https://simg.open-open.com/show/e509a934685bf653c68134bb470ed672.png"></p>    <h2>主线程判断</h2>    <p>判断当前执行线程是否为主线程的方法有很多,比如</p>    <pre>  <code class="language-objectivec">[NSThread isMainThread]  pthread_main_np  </code></pre>    <p>在RN中,它是这样的</p>    <pre>  <code class="language-objectivec">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;  }  </code></pre>    <p>当然,由于无法查看NSThread内部实现机制,暂时无法了解孰优孰劣,不过,[NSThread isMainThread]貌似足矣!</p>    <h2><strong>volatile不简单</strong></h2>    <p>在 <a href="/misc/goto?guid=4959675399872056093" rel="nofollow,noindex">百科</a> 中,是这样描述它的:就像大家更熟悉的const一样,volatile是一个类型修饰符,它是被设计用来修饰被不同线程访问和修改的变量。作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值</p>    <p>简单说,被volatile修饰的变量是多线程安全的,其次,不会因为编译器优化导致读值出错。关于编译器编译优化可以看看这篇 <a href="/misc/goto?guid=4959675399994465323" rel="nofollow,noindex">文章</a></p>    <p>iOS开发中确保多线程安全的方法有很多,原子操作、线程锁、单线程执行等等,本人也写过相关文章iOS开发多线程同步</p>    <p>在RN中,通过volatile修饰符,巧妙实现了多线程取消操作</p>    <pre>  <code class="language-objectivec">__block volatile uint32_t cancelled = 0;  if (!cancelled) {     // to do...  }  OSAtomicOr32Barrier(1, &cancelled);  </code></pre>    <p>通过原子性操作访问被volatile修饰的cancelled对象即可保障函数只执行一次。想想大家熟悉的单例dispatch_once_t,现在让你设计单例对象,你又会如何设计了?</p>    <pre>  <code class="language-objectivec">+ (instancetype)sharedInstance {    static RCTWebSocketManager *sharedInstance = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{      sharedInstance = [self new];    });    return sharedInstance;  }  </code></pre>    <h2>结构体Struct</h2>    <p>说起Struct,不知各位对它印象如何?大学C课本中学过?NSObject类class原型貌似有讲?</p>    <pre>  <code class="language-objectivec">struct iOSDev {      NSString *nickName;  };  </code></pre>    <p>OC中一个简单的结构体,在Swift中,Struct也可以这样写</p>    <pre>  <code class="language-objectivec">struct iOSDev {      var nickName : String      func getBusinessCard() -> String {          return "\(nickName),幽默的iOS开发者!"      }  };     let iOSOlinone = iOSDev(nickName: "olinone")  print(iOSOlinone.getBusinessCard())  </code></pre>    <p>getBusinessCard为结构体函数,是不是感觉很方便!其实OC中也可以这样写</p>    <pre>  <code class="language-objectivec">struct iOSDev {      NSString *nickName;      NSString *getBusinessCard() {          return [NSString stringWithFormat:@"%@,幽默的iOS开发者!", nickName];      }  };     iOSDev iosDev = iOSDev{@"olinone"};  NSLog(@"%@", iosDev.getBusinessCard());  </code></pre>    <p>当然,为Struct添加函数并不是C语言特性,而是C++特性,因此,为了编译通过,你需要将.m文件修改成.mm文件</p>    <p>Struct有其使用的特殊场景,相较于Class,合理的使用Struct可以使代码更加整洁。同时,为了适应Swift中Struct强大特性,可以试着在OC项目中尝试Struct</p>    <p>最后,给大家来个段子吧:</p>    <p><em>话说一美女要在两个男人之间做选择,一个年纪大,长的丑,是个千万富翁,另一个年轻,帅气,iOS开发程序猿。 她对他们说,我会给你们一人一张纸条,写着我愿意的那张就是我的选择。 富翁打开纸条,看见上面写着我愿意,于是搂着她,坐上豪车高兴的走了。 年轻的小伙很伤心,打开纸条看见上面写着:“等我一个月~”  ^o^</em></p>    <p>写在文后:</p>    <p>有些童鞋可能经常会问一个问题,感觉自己技术遇到瓶颈,如何才能进一步提升自己技术能力?其实这个问题,本人也是摸石头过河,不过有一点可以确定,那就是保持一颗不断进取的心吧</p>    <p>新建了一个iOS开发QQ交流群(首页右上角入群),欢迎广大iOS开发朋友一同交流学习。当然 <em> ,你也可以Follow本人 <a href="/misc/goto?guid=4959675400109443733" rel="nofollow,noindex">GitHub</a> ,或者关注我的新浪微博,感谢你的来访,下期再见! </em></p>    <p> </p>    <p><a href="/misc/goto?guid=4959675400225364634">阅读原文</a></p>    <p> </p>