iOS推送开发时,NSData格式的deviceToken转NSString的方法

1,686 阅读2分钟

推送开发时通过方法didRegisterForRemoteNotificationsWithDeviceToken获取到的DeviceToken转成NSString, 可以用来测试推送是否可用

之前网上查找的方法都是使用NSData的descriptio属性来获取到NSString, 然后去掉大小于号跟空格:

NSString *strToken1 = [[[deviceToken.description stringByReplacingOccurrencesOfString: @"<" withString: @""]
                        stringByReplacingOccurrencesOfString: @">" withString: @""]
                       stringByReplacingOccurrencesOfString: @" " withString: @""];

但是今天调试的时候发现这样, 得到的strToken1打印在控制台上, 是这样的格式:

po strToken1
{length=32,bytes=0x654d21d6e832f37a53f517ec77e2e0b6...手动打码0f1031a4}

中间被 ... 省略了.根本没办法复制去调试. 于是, 换个思路, 其实直接打印deviceToken对象可以得到:

po deviceToken
<654d21d6 e832f37a 53f517ec 77e2e0b6 06dc49bb 手动打码 手动打码 0f1031a4>

通过description属性, 去掉两端大小于号跟空格, 太简单粗暴了,而且不能保证未来一直这样不会出错, 比如现在打印不出来结果就尴尬了. 最好的方法就是直接去按字节读取一边Data然后十六进制输出就行了嘛, 走起:

//OC写法:
UInt8 buffer;//一个个字节读取
NSString *strToken = @"";
for (NSInteger idx = 0; idx < deviceToken.length; idx ++) {
    //一次读一个字节, 读一个就写入strToken里一个
    [deviceToken getBytes:&buffer range:NSMakeRange(idx, 1)];
    //注意补0
    NSString *strBuffer = [NSString stringWithFormat:@"%02x", buffer];
    strToken = [strToken stringByAppendingString:strBuffer];
}
//Swift写法:
//一句话搞定...真香! 原理: 转成UInt8数组, map转成16进制String, 然后joined成String
let strToken = Array<UInt8>.init(deviceToken).map({String.init(format: "%02x", $0)}).joined(separator: "")

输出看看:

po strToken
654d21d6e832f37a53f517ec77e2e0b606dc49bb手动打码手动打码0f1031a4

完美~

突发奇想:

  • 一个字节一个字节读太慢了,是否可以两个字节或者4个,8个字节, 用UInt16, UInt32, UInt64读取呢?岂不是快很多? 实践:
//OC写法:
UInt16 buffer2;
NSString *test2 = @"";
for (NSInteger idx = 0; idx < deviceToken.length; idx += sizeof(UInt16)) {
    [deviceToken getBytes:&buffer2 range:NSMakeRange(idx, sizeof(UInt16))];
    NSString *strBuffer = [NSString stringWithFormat:@"%04x", buffer2];
    test2 = [test2 stringByAppendingString:strBuffer];
}

输出看看:

4d65d62132e87af3f553ec17e277b6e0dc06bb49手动打码手动打码100fa431

两两字节反了...因为iOS是小端模式存储的数据, 低位数据会被存放在低位地址, 当一次读两个字节时, 自动会把高地位互换读取, 比如对于字节: 0x4d65, 读取两次一字节打印可以输出4d65,但是如果一次读取2字节, 就会被当作654d读取输出. 因此如果想要一次多度几个字节来加快读取速度, 记得手动换位.

延伸:

  • 判断大小端模式:
//两字节int 放个1, 如果是小端, 会把1存放在低地址位, 然后读低地址字节
//读到1说明是小端, 否则是大端
UInt16 t = 1;
char *p = (char*)&t;
if (*p == 1) {
    printf(@"小端");
} else {
    printf(@"大端");
}
  • 验证小端模式:
    //一个个字节写入
    UInt8 tint;
    NSMutableData *writer = [NSMutableData new]; //打印writer: <11223344>
    tint = 0x11;
    [writer appendBytes:&tint length:1];
    tint = 0x22;
    [writer appendBytes:&tint length:1];
    tint = 0x33;
    [writer appendBytes:&tint length:1];
    tint = 0x44;
    [writer appendBytes:&tint length:1];
    //用4字节读取
    UInt32 iread;
    [writer getBytes:&iread range:NSMakeRange(0, 4)];
    NSString *strIread = [NSString stringWithFormat:@"%08x", iread]; //输出44332211

如有错误, 欢迎评论指出~谢谢~