升级了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了,堆栈如下
什么鬼,设置locale也会错?那不设置了行不行捏?也不行!
从上图会看到一个很奇怪的堆栈,似乎是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这个字符串,然后调用函数,再设置属性,所以就猜测,是不是要先初始化UIDatePicker的timeZone才行。
修正后的代码如下,跑通了。我觉得这个问题可能我还是没找到最终的根源,但是给我带来了另外一个解决问题的思路,就是仔细考虑一下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了,这种必须要显式设置才能好的逻辑真的不知道是哪里影响了。
更为奇怪的是,NSTimeZone的systemTimeZone方法,提示是IOS10以后的方法,WTF?!谁能解释一下这个疯狂的IOS?!