32-bit 64-bit

2015年初,所有提交审核的App需要适配64位,目的是充分利用ARM处理器的高性能支持,让应用能够有更为极致的体验。在iPhone5S以后的设备都是支持64位的。

一、时间显示引发的问题

由于32位处理器的NSInteger是int类型的,导致溢出,下面我们来具体看一下。

1、服务端返回JSON数据

服务端返回日期数据为13位时间戳。

"count": 0, "data": { "nowDate": 1508470153576, //13位时间戳 "skuCommission": { "actPrice": 89, "commissionRate": 0.1, "endTime": 1508547599000, //13位时间戳 "isDelete": 0, "limitCommissionRate": 0, "limitInventory": 0, "limitUseCoupon": false, "skuId": 0, "specialSell": true, "startTime": 1508461200000, //13位时间戳 "type": 1 "saleInfo": { "endTime": 1508547599000, //13位时间戳 "price": 89, "startTime": 1508461200000 //13位时间戳 "isRedirect": 0, "isSuccess": 1, "login": 0

2、iOS客户端Model数据模型

客户端通过MJExtension(数据转模型开源库),解析为Model模型。
修改前:时间戳使用NSInteger类型
修改后:时间戳使用double类型(或者 long long类型)

@interface ProductDetailModel : NSObject
#@property (nonatomic, assign) NSInteger nowDate;               //修改前
@property (nonatomic, assign) double nowDate;                   //修改后
@interface ProductSkuCommission : NSObject
@property (nonatomic, assign) CGFloat actPrice;
@property (nonatomic, assign) CGFloat commissionRate;
#@property (nonatomic, assign) NSInteger endTime;               //修改前
@property (nonatomic, assign) double endTime;                   //修改后
@property (nonatomic, assign) NSInteger gmtCreate;
@property (nonatomic, assign) NSInteger gmtModified;
@property (nonatomic, assign) NSInteger isDelete;
@property (nonatomic, assign) NSInteger limitCommissionRate;
@property (nonatomic, assign) NSInteger limitInventory;
@property (nonatomic, assign) NSInteger skuId;
@property (nonatomic, assign) BOOL specialSell;
#@property (nonatomic, assign) NSInteger startTime;             //修改前
@property (nonatomic, assign) double startTime;                 //修改后
@property (nonatomic, assign) NSInteger type;
@interface ProductSaleInfo : NSObject
#@property (nonatomic, assign) NSInteger endTime;               //修改前
@property (nonatomic, assign) double endTime;                   //修改后
@property (nonatomic, assign) CGFloat price;
#@property (nonatomic, assign) NSInteger startTime;             //修改前
@property (nonatomic, assign) double startTime;                 //修改后

3、出现溢出问题

❌时间戳转换成时间后,显示异常❌,如下:

// 原始数据
1508547599000
// 64位架构,使用NSInteger
1508461200000 10月20日09点
// 32位架构,使用NSInteger
927679104     01月12日01点

二、排查问题

排查了网络返回数据,用户设备时区,使用设备机型,使用设备系统,是否越狱,转换时间方法等等,后来发现在iPhone5,iPhone5C设备中存在该问题(目前App兼容iOS8以上,暂时不考虑iPhone5以下机型),其他设备是OK的,考虑问题出在机型之间存在的差异。

1、通过XCode,按住「Command」点击「NSInteger」查看NSInteger的定义如下:

32位CPU上,将NSInteger定义为了int类型
64位CPU上,将NSInteger定义为了long类型

2、通过Dash,查看Apple官网API文档,查看NSInteger的定义如下:

构建32位应用,NSInteger32位整型
构建64位应用,NSInteger64位整型

注意:由于long和NSInteger的字节数变了,所以在兼容的时候可能会导致溢出

相同类型在不同处理器中的差异:

类型32位 处理器64位 处理器
NSIntegerintlong
NSUIntegerunsigned intunsigned long
~~~
int32bit(4字节)32bit(4字节)
long32bit(4字节)64bit(8字节)
long long64bit(8字节)64bit(8字节)

变量范围:

整型有符号 范围无符号 范围
4字节-2147483648 ~ 21474836470 ~ 4294967295
8字节-9223372036854775808 ~ 92233720368547758070 ~ 18446744073709551615

三、为什么用NSInteger,不直接用int或long呢?

现在,Xcode可以支持开发32位和64位的应用,为了让开发者写出在其他系统上也能够运行的代码,苹果公司引入了NSInteger类型以及NSUInteger类型。这两种类型无论在32位系统还是64位系统上都能够通用。

NSInteger以及NSUInteger类型在苹果的类库中被广泛使用,所以写Objective-C的时候,会经常使用它们。

用NSInteger 声明的整型变量,你不用关心是32位还是64位不用关心是声明int还是longiOS编译器会自动分配完成

四、总结:

一个10位以上的整数。
64位系统中,使用NSInteger或者long类型,是可以正常存储的 。
32位系统中,由于字节数变了,导致溢出了。
兼容32位64位系统,使用intlong long(或者int32_tint64_t)这样的数据类型,比使用NSInteger要更加可靠,具体问题具体分析。

最后,使用了double代替NSInteger来解决该问题

..UNIX时间戳是一种表示时间的方法,广泛用于计算机系统和网络协议中。它定义的时间起点是1970年1月1日午夜(协调世界时UTC),也就是所谓的“UNIX纪元”开始的时刻。 将 13 整数的毫秒时间戳化成本地普通时间 (datetime格式)将 10 整数的秒级时间戳化成本地普通时间 (datetime格式)将 13 整数的毫秒级时间戳化成 本地普通时间 (字符串格式)将 10 整数的秒级时间戳化成 本地普通时间 (字符串格式) var year = aa.getFullYear(); var month = (aa.getMonth() + 1) < 10 ? "0" + (aa.getMonth() + 1) : ""; var day = (aa.getDay()) < 10 ? "0" + (aa.getDay()) : ""; var endTime = year + "-" + month + "-" + day; console.log 在32位的机子上,time_t是个4字节的值,存储的是1900年到现在的秒数,最大可存放的秒数(时间戳)到2038年多,设置的tm_year不能超过138,多了会溢出,返回异常值。 解决方法: 修改mktime函数的返回值类型 unsigned long -> unsigned long long unsigned long long my_mktime(const unsigned int year0, const unsigned int mon0, const unsign