关于UIDatePicker的一个奇怪问题

升级了Xcode8以后,发生了很多很奇怪的事情,譬如说注释快捷键失效的问题,然鹅我们的项目遇到了更加神奇的问题,是跟UIDatePicker这个系统控件相关。
先上代码

UIDatePicker *datePicker = [[UIDatePicker alloc] init];
//设置本地语言
datePicker.locale = [NSLocale localeWithLocaleIdentifier:@"zh"];
//设置日期显示的格式
NSDate *curDate = [NSDate date];
[datePicker setDate:curDate animated:NO];
datePicker.datePickerMode = UIDatePickerModeDate;
//设置accountTextField的inputView控件为datePicker
self.accountTextField.inputView = datePicker;

一段很简单很普通的代码,就莫名奇妙地crash了,堆栈如下

crash堆栈

什么鬼,设置locale也会错?那不设置了行不行捏?也不行!

crash堆栈

从上图会看到一个很奇怪的堆栈,似乎是UIDatePicker在设置amString的时候有什么东东有问题才导致的crash,而且命令行也有一个error输出:
-[__NSCFConstantString name]: unrecognized selector sent to instance 0x109bd8900

google搜索了一通,并没有人说到这个问题,而且更让人崩溃的是,自己新建一个demo工程跑代码完全没问题,一用到我们的工程上面就不行了。考虑了一下,说不定是build setting有问题或者引入了奇怪的第三方库,尝试了1个多小时,放弃了,感觉这个方向工作量太大,而且也应该不是直接原因。这个时候静下心来,再分析一下crash堆栈,还是看出一点线索了

汇编代码

留意一下箭头方向的那一坨汇编,是这样子的

    0x10ae74dc6 <+346>:  leaq   0x2e7263(%rip), %rdx      ; @"timeZone"
    0x10ae74dcd <+353>:  movq   %rbx, %rsi
    0x10ae74dd0 <+356>:  callq  *0x2c49ca(%rip)           ; (void *)0x000000010e7bfac0: objc_msgSend
    0x10ae74dd6 <+362>:  testq  %rax, %rax
    0x10ae74dd9 <+365>:  je     0x10ae74df1               ; <+389>
    0x10ae74ddb <+367>:  movq   (%r12,%r14), %rdi
    0x10ae74ddf <+371>:  movq   0x2c4782(%rip), %rcx      ; (void *)0x000000010efd71d0: kCFDateFormatterTimeZone
    0x10ae74de6 <+378>:  movq   (%rcx), %rsi
    0x10ae74de9 <+381>:  movq   %rax, %rdx
    0x10ae74dec <+384>:  callq  0x10b0aedc6               ; symbol stub for: CFDateFormatterSetProperty
    0x10ae74df1 <+389>:  movq   (%r12,%r15), %rdi

这段代码的意思估计大家也大致看得懂,首先初始化timeZone这个字符串,然后调用函数,再设置属性,所以就猜测,是不是要先初始化UIDatePickertimeZone才行。

修正后的代码如下,跑通了。我觉得这个问题可能我还是没找到最终的根源,但是给我带来了另外一个解决问题的思路,就是仔细考虑一下crash堆栈的汇编相关代码,说不定就能找到一些特别的解决思路了。

UIDatePicker *datePicker = [[UIDatePicker alloc] init];
//跟随系统时区
datePicker.timeZone = [NSTimeZone systemTimeZone];//一定要先设置这个,不然会crash
//设置本地语言
datePicker.locale = [NSLocale localeWithLocaleIdentifier:@"zh"];
//设置日期显示的格式
NSDate *curDate = [NSDate date];
[datePicker setDate:curDate animated:NO];
datePicker.datePickerMode = UIDatePickerModeDate;
//设置accountTextField的inputView控件为datePicker
self.accountTextField.inputView = datePicker;

奇怪的是,这个属性本身系统就已经设置为nil了,这种必须要显式设置才能好的逻辑真的不知道是哪里影响了。

timeZone属性

更为奇怪的是,NSTimeZonesystemTimeZone方法,提示是IOS10以后的方法,WTF?!谁能解释一下这个疯狂的IOS?!

systemTimeZone

推荐阅读更多精彩内容