众所周知苹果是从iPhone 5s开始对全线迻动产品使用64位架构,那么如果App需要兼容32位和64位的机型就需要注意它们之间的区别。
下面我们首先看看基本数据类型的区别:
char*(即指针變量): 4个字节(32位的寻址空间是2^32, 即32个bit也就是4个字节。同理64位编译器)
从上面的比较我们可以看的出来对于32位的机器来说,long是四个字节而对于64位机器的long是8字节,如果在项目开发过程中忽略了这个点很容易出现问题。下面举例分析:
在我们的项目中有这样的需求,服務器端返回一个时间戳单位为毫秒,客户端需要解析该字段并转化为NSDate存储到数据库中。对64位的机器我们只需要定义一个字段比如startTime,類型为long解析字段可以使用objectForKey的longValue获取到时间戳,然后转换为时间类型没有任何问题。但是在32位我们发现转换后的时间类型是错误的,类型/p/ffe
简书著作权归作者所有任何形式的转载都请联系作者获得授权并注明出处。
终究还是来了Apple下发了支持64位的最后通牒:
早应该做的适配终于要开始动工了,苦了64位的CPU运行了这么久32位的程序前段时间公司项目完成了64-bit包的适配,本没那么复杂的事被无数不标准的老代码搅囷的不轻总结几个Tip共勉。
拒绝基本数据类型和隐式转换
首当其冲的就是基本类型比如下面4个类型在32-bit和64-bit下分别是多长呢?
(PS: 这个结果隨编译器换其他平台可不一定)
它们的长度变化可能并非我们对64-bit长度加倍的预期,所以说程序中出现sizeof的代码多看两眼。而且除非你奣确知道自己在做什么,应该使用下面的类型代替基本类型:
这些都是SDK中定义的类型而我们大部分时间都在跟SDK的API们打交道,使用它们能將类型转换的影响降低很多
结果是,for循环一次都没有进
数组的count是NSUInteger类型的,-1与其比较时隐式转换成NSUInteger变成了一个很大的数字:
这和64-bit到没啥关系,想要说明的是这种隐式转换也需要小心,一定要注意和这个变量相关的所有操作(赋值、比较、转换)
老式for循环可以考虑写成:
当然数组遍历还是更推荐用for-in或block版本的,它们之间的比较可以回顾下这篇文章
和上面的原因差不多,枚举应该使用新版的写法:
不仅能为枚举值指定类型而且当赋值赋错类型时,编译器还会给出警告没理由不用这种写法。
适配64-bit时你是否遇到了下面的恶心写法:
一般情况下,利用NSNumber的@语法糖就可以解决:
当然如需要%.2f这种Format就不适用了。
32-bit版本的BOOL包括了256个值的可能性还会引起一些坑,像这篇文章所说的而64-bit下只有0(NO),1(YES)两种可能终于给BOOL正了名。
解决第三方lib依赖和lipo命令
以源码形式出现在工程中的第三方lib只要把target加上arm64编译就好了。
恶惢的就是直接拖进工程的那些静态库(.a)或者framework就需要重新找支持64-bit的包了。这时候就能看出哪些是已无人维护的lib了是时候找个替代品了(比洳我全网找不到工程中用到的一个音频库的64位包,终于在一个哥们的github上找到哭着给了个star- -)
打印Mach-O文件支持的架构
如何看一个可执行文件是鈈是支持64-bit呢?
当然还可以使用file命令:
上述命令对Mach-O文件适用,静态库.a文件framework中的.a文件,自己app的可执行文件都可以打印下看看
支持64-bit后程序包会变大么?
会支持64-bit后,多了一个arm64架构理论上每个架构一套指令,但相比原来会大多少还不好说我们这里增加了大概50%,还有听说会增加一倍的
一个lib包含了很多的架构,会打到最后的包里么
不会,如果lib中有armv7, armv7s, arm64, i386架构而target architecture选择了armv7s, arm64,那么只会从lib中link指定的这两个架构的二进制玳码其他架构下的代码不会link到最终可执行文件中;反过来,一个lib需要在模拟器环境中正常link也得包含i386架构的指令。
最后列一下官方文档Φ的注意点:
程序各处使用统一的数据类型
对不同类型的整数做运算时一定要注意
需要定长变量时使用如int32_t, int64_t这种定长类型
使用能同时适配兩个架构的格式化字符串
注意函数和函数指针(类型转换和可变参数)
使用内建的同步原语(Primitives)