1数字溢出
背景
写iOS代码经常需要在某处计算时间戳。通常时间戳是格林威治时间(即零时区时间)1970年1月1日0点至当前的毫秒数。例如北京时间(东八区)2015-7-4 10:35:6,的时间戳是1435977306000。就目前来说它是个13位数字。
现象
某种时间戳转为string的方式如下。
1 | long st = [[NSDate date] timeIntervalSince1970] * 1000; |
用5s及以上的模拟器跑都能得到正确的结果。而用5s以下的模拟器跑都输出错误的值。
解释
产生该问题的原因如下:对于iOS的64位应用,long是8byte整数,涵盖-9223372036854775808 到 +9223372036854775807。而32位的iOS应用,long是4byte整数,涵盖-2147483648 到 +2147483647。32位下long最多只能表征10位正数,而我们的时间戳是13位的,显然数字溢出了。而对于64位的iOS应用long能表示19位正数,这是毫无压力的。详见iOS上应用如何兼容32位系统和64位系统
解法
我们观察到不管是IPL32规范还是IPL64规范,long long都是以8byte存储,能表征时间戳。所以此问题的正确姿势是用long long来存储时间戳。正确代码如下:
1 | long long st = [[NSDate date] timeIntervalSince1970] * 1000; |
2NSDictionary使用不当
背景
iOS客户端跟服务器进行通信通常采用json格式。客户端从服务器上获取数据后,使用NSJSONSerialization把json串变为NSDictionary。早期,在不使用Mantle等第三方库做NSDictionary到Model转换的时候,我们需要自己去把Dic模型化。NSDictionary中的key是一个符合NSCopying协议的objc对象,通常我们用NSString作为Dic的key。NSDictionary中的value需要是一个objc对象。
现象
某次Dic模型化转换如下。其中,AppData是需要转换的模型。webType是一个BOOL量。发现该BOOL一直为YES。不受wap_type的控制。
1 | AppData *appData = [[AppData alloc] initWithAppId:[dic[@"app_id"] integerValue] |
解释
(BOOL)dic[@”wap_type”]
这种写法 ,是取出dic中@”wap_type”这个key的value,该value是一个objc对象,把该对象强制转换为BOOL类型。因此,只要dic中含有 @”wap_type”这个key对应的value的时候会是YES,否则是NO。它不会考虑这个value具体是YES还是NO。
解法
[dic[@”wap_type”] integerValue]
取出dic中@”wap_type”这个key的value,获取该value的integerValue。
3NSArray NSMutableArray相关问题
背景
NSArray和NSMutableArray是常用的objc容器,该容器常用于存储objc对象。
现象
1 | //例1 |
例1
对一个空NSArray(注意空NSArray!!!不是nil NSArray,两者不是一样的东西),访问第一个和最后一个对象,都会抛出 NSRangeException。如果不抓异常,程序会崩溃。
例2
**[ary addObject:[weakTestObj createObj]]**这函数调用有时候会抛出NSInvalidArgumentException。如果不抓异常,程序会崩溃。
解释
对于例1,NSArray通过index访问对象,如果index超限那么会抛出异常。这种函数的函数名一般都有一个AtIndex。这一点是要记住的!!!
对于例2,weakTestObj是一个weak对象,在其指向的对象被销毁后,weakTestObj可能是nil。那么,[weakTestObj createObj]函数会返回nil,在此种情况下[ary addObject:[weakTestObj createObj]]可能会抛出异常。
解法
这里给出一些常用的解决方案:
1)index访问对象通过条件语句判断index是否在合理范围内。NSArray的第一个对象访问推荐用firstObject属性,最后一个对象访问推荐用lastObject属性。;
2)给NSMutableArray添加对象要做nil判断;
3)用try catch包一下,设置异常处理逻辑。(终极杀招)
4NSString NSMutableString相关问题
背景
在项目中经常需要比较两个string是否相等,取某个string的sub string,对一个mutable string添加string。
现象
1 | //例1 |
例1
输出:不等、相等
例2
抛出非法参数异常
例3
抛出范围异常
解释
例1,两个string比较其内容是否相同要用*- (BOOL)isEqualToString:(NSString )aString方法。不要直接用”==”操作符比较。”==”操作符比较的是操作符两边的oc对象是否是同一个oc对象,这个问题可以从c语言指针的角度来思考。而*- (BOOL)isEqualToString:(NSString )aString是类似于c语言的库函数**int strcmp(const char str1, const char str2)。
例2和例3,其实跟上述NSArray NSMutableArray相关问题类似。访问之前判断范围,添加内容判断nil。
解法
1)NSString判等用*- (BOOL)isEqualToString:(NSString )aString;
2)NSString和NSMutableString操作的时候要注意异常处理,一般含有Index的这种要注意范围异常,append这种要注意传参异常;当然终极杀招依旧是try catch
5表里不一,看起来是个NSString对象,实际上是其他对象
背景
由于项目原因,针对id号这种参数,早期服务器传参可能使用的是string。而服务器端代码重构后,是使用整数传参。
现象
1 | NSString *topicId = dic[@"tId"]; //dic是NSJSONSerialization解析json串得到的字典 |
调用上述代码后,程序会崩溃。
解释
由于服务端重构,把json中的string变成了int,所以*NSString topicId = dic[@”tId”]此处实际得到的是一个NSNumber对象,对NSNumber调用length方法会出现未知选择子崩溃。
解法
1)树立信念,不要相信服务器会100%传好数据给你,即使正确率有99.9%那剩下的0.1%就是异常;
2)从服务器上得到的数据总应该进行类型判断,具体做法是用**- (BOOL)isKindOfClass:(Class)aClass或- (BOOL)isMemberOfClass:(Class)aClass**这两种方法;
3)用try catch包,做异常处理(终极解法)。