React Native 一处诡异 crash: RCTFont SIGABRT

目前在做的一个项目迁移到 React Native 已经一年多了,也意味着踩了一年的坑,感觉光填上各种奇怪的坑都会让自己的水平提升不少。最近解决了一个占比接近 10% 的崩溃,在这里记录一下。

让 WKWebView 支持 NSURLProtocol

最近把公司的项目从 UIWebView 迁移到了 WKWebView,因为之前大体上还是遵从了 Apple 的 API 没有过度地去 hack,而且 WebViewJavascriptBridge 也同样支持 WKWebView,所以迁移过程没有想象中那么痛苦,只要把 UIWebViewDelegate 的方法改成 WKUIDelegate 和 WKNavigationDelegate 对应方法就好了。

UIWebView 与 3D Touch 的自定义交互

0x00 3D Touch API 处理 UIWebView 的局限

从 iOS 9 开始,UIKit 新增了 3D Touch 相关接口,如果使用苹果推荐的 storyboard 搭建 UI,勾选了 Preview & Commit Segues 选项之后就可以零代码实现系统级的 3D Touch 效果;用代码实现也很简单,只要实现 UIViewControllerPreviewingDelegate 协议,然后调用 - [UIViewController registerForPreviewingWithDelegate:sourceView:] 方法,就可以对任意 UIView 进行 Peek 和 Pop 操作了。

实际上,尽管 - [UIViewController registerForPreviewingWithDelegate:sourceView:] 方法的第二个参数接受的是任意的 UIView,但在 UIWebView 和 WKWebView 上按压的操作却是没有效果的。虽然苹果针对这两个 WebView 提供了 allowsLinkPreview 属性做了特殊处理,但这也仅仅是调用了 Safari 打开链接,实际应用中经常要针对某些特殊的链接进行应用内跳转,这是 allowsLinkPreview 无论如何也完成不了的。

View Controller 转场实现机制分析

众所周知,iOS 的 View Controller 的转场效果本质上是基于“当前视图消失和下一视图出现”所进行的动画。如果是自己实现的 UIViewControllerAnimatedTransitioning 协议,那么此动画就由 - animateTransition: 方法来提供;如果没有实现此协议则此动画由系统提供。以结构如下图的 Navigation Controller 为例:

Navigation Controller 结构

在 iOS APP 崩溃时弹出友好提示框

昨天补了 iOS RunLoop 相关的基础知识,在一部讨论 RunLoop 实现细节的视频的最后面,@sunnyxx 讲到了一个很有意思的黑科技————“让 App 在 Crash 的时候回光返照”,内容大致如下:

1
2
3
4
5
6
7
8
9
10
11
//取当前 run loop
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
//取 run loop 所有运行的 mode
NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runLoop));
while (1) {
for (NSString *mode in allModes) {
//在每个 mode 中轮流运行至少 0.001 秒
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
}
}
>

对于因为接收到 crash 的 signal 而挂掉的程序,可以在接收到 crash 的信号之后重新起一个 run loop 然后跑起来。但是这个并不能保证 app 能像原来一样能正常运行,只能是利用它来在奄奄一息的状态下弹出一些友好的错误信息。

检测 iOS 项目中的内存泄漏

一般来说,在 ARC 环境下,只要在使用 delegate、NSTimer、block 的时候注意一下不要出现循环引用,那么 Objective-C 对象的内存泄漏问题就可以轻松避免。

但是在实际项目中,一些错误的结构设计可能会导致难以发现的泄漏问题,比如像 A -> B -> C -> ... -> A 这种长环的循环引用,或者一个实例被一个 单例 持有,在 review 的时候可能会漏掉这些问题,这时就需要流程化的方式来检测了。

给 Objective-C 中的 Protocol 加上默认的实现

0x01 Abstract Class

Java、C++ 等 OOP 语言有一个抽象类的概念,即一个类实现了部分方法,另一部分的方法必须由继承它的子类来实现。Objective-C 在设计上没有这个概念,转而提供了用途类似的 协议,除了不能给方法加默认实现以外,与抽象类的用法大体相同。但是在实际项目中,让一个协议实现一些共通的方法还是很有必要的,比如很多类都遵守了某一个协议,而这个协议中某一个方法的实现大体上都一样的时候,在每一个子类内部都 copy 一份同样的代码就不太合适了。

扩展 UIButton 和 UICollectionViewCell 的响应区域

0x01 前言

问题由一个项目需求引起。设计MM给的图大概像这样:

snapshot

如上图所示,列表的内容由服务器传回,且用户可编辑,显然这样的界面应该用 UICollectionView 来搭建。实现关闭按钮则需要在 UICollectionViewCell 的右上角添加一个 UIButton,并且要将 Cell 的 clipsToBounds 属性设置为 NO 以避免按钮被切掉一部分。

界面搭建好了,但默认状态下 UIButton 的点击响应范围跟它的显示区域一样小,导致这个按钮很难被点到,因此首先要解决的是这个按钮的热区扩展问题。

使用 Objective-C Runtime 解决 unrecognized selector 错误

0x01 前言

NSOperation 类有一个属性 name,用以标记一个 NSOperation 对象。苹果提供这个属性的本意是为了调试方便,但实际上通过它我们还可以简便地实现一些业务需求,比如加入 NSOperationQueue 前检查去重和排序什么的。

但很可惜,这个属性是 NS_AVAILABLE(10_10, 8_0) 的,换言之如果在 iOS 7 以下的系统上使用这个属性的话,控制台会打印这样一行错误:

unrecognized selector sent to instance

如果项目需要兼容 iOS 7 系统的话,我们就需要寻找一种方法,在 iOS 7 上也能方便地标记一个 NSOperation 。

使用git log命令自动生成周报

2016-3-15 update: 项目已提交至GitHub,地址https://github.com/yeatse/git-log-weekly-report/


最近公司主管突然让我们每周末发周报了,具体就是总结一下上周都干了什么之类的。从小学开始就经常不交作业的我心里是一百个不愿意,但是这个是跟绩效挂钩的,人总不能跟钱过不去嘛,没办法只能养成习惯去写周记了。

当然,作为一个懒惰的程序员,每周让我拿出半个小时的时间去整理周报这种事我是不会去做的。好在现在的项目是用git作为版本控制工具的,用git log命令的话,只要一行就可以在屏幕输出日志内容了:

1
$ git log --author="yeatse" --format="%cd : %s" --since=last.Monday --reverse --no-merges --date=format:'%F %T'

具体的参数意义在git-log Documentation上可以找到(其中date format选项需要升级git版本到v2.6.0rc以上才有效)。输出的结果大概是这个样子:

1
2
3
4
5
2016-02-29 22:04:54 : Update elctron-prebuilt version. Fix #11.
2016-02-29 22:49:51 : Optimize user's avatar display in Linux.
2016-02-29 23:03:20 : Disable zooming in the app.
2016-03-01 00:23:32 : Introduce travis CI.
2016-03-01 00:28:14 : Fix tar-all chmod.