为什么应用程序在 NSData getBytes 崩溃?
Why does app crash at NSData getBytes?
扩展NSData判断文件类型:
extension NSData {
var dataType: String? {
// Ensure data length is at least 1 byte
guard self.length > 0 else { return nil }
// Get first byte
var c = [UInt8](count: 1, repeatedValue: 0)
self.getBytes(&c, length: 1)
// Identify data type
switch (c[0]) {
case 0xFF:
return "jpg"
case 0x89:
return "png"
case 0x47:
return "gif"
case 0x49, 0x4D:
return "tiff"
default:
return nil //unknown
}
}
}
在从服务器获取的图像数据的 NSData
对象上调用上述方法。
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
do {
// Fetch image synchronously from server
let query = PFQuery(className: <...>)
let result = try query.getFirstObject()
guard
let imageObject = result.objectForKey(<...>) as? PFFile,
let imageData = try? imageObject.getData(),
let image = imageData.dataType == "gif" ? UIImage.animatedImageWithAnimatedGIFData(imageData) : UIImage(data: imageData)
else {
return
}
<...>
} catch (let error as NSError) {
<...>
}
}
然而,应用程序很少在第 self.getBytes
行崩溃:
这是什么原因?
getBytes 的缓冲区是 &c
,一个 UnsafeMutablePointer
- 因此我是否必须考虑任何特殊的内存?
更新
以下代码变体仍然会发生崩溃:
// Get first byte
var c: UInt8 = 0;
self.getBytes(&c, length: 1)
更新
以下代码变体仍然会发生崩溃:
// Get first byte
var c = [UInt8](count: 1, repeatedValue: 0)
c.withUnsafeMutableBufferPointer {
buffer in
getBytes(buffer.baseAddress, length: 1)
}
guard c.indices.contains(0) else { return nil }
我遇到了以下崩溃并包含了整个线程,也许有人能发现提示:
Thread 18 Crashed:
0 libsystem_platform.dylib 0x21a8e198 _platform_memmove$VARIANT$CortexA9 + 92
1 Foundation 0x22512923 __34-[_NSDispatchData getBytes:range:]_block_invoke + 176
2 libdispatch.dylib 0x218d238d _dispatch_data_apply + 82
3 libdispatch.dylib 0x218d4a51 dispatch_data_apply + 26
4 Foundation 0x22512865 -[_NSDispatchData getBytes:range:] + 86
5 Foundation 0x2267730b -[_NSDispatchData getBytes:length:] + 24
6 MyAppName 0x00079ba0 partial apply forwarder for (extension in MyAppName):__ObjC.NSData.(dataType.getter : Swift.String?).(closure #1) (NSData+Extension.swift:54)
7 MyAppName 0x00079c14 partial apply forwarder for reabstraction thunk helper from @callee_owned (@inout Swift.UnsafeMutableBufferPointer<Swift.UInt8>) -> (@unowned (), @error @owned Swift.ErrorType) to @callee_owned (@inout Swift.UnsafeMutableBufferPointer<Swift.UInt8>) -> (@out (), @error @owned Swift.ErrorType) (NSData+Extension.swift:0)
8 MyAppName 0x00079cb8 generic specialization <Swift.UInt8, ()> of Swift.Array.withUnsafeMutableBufferPointer <A> ((inout Swift.UnsafeMutableBufferPointer<A>) throws -> A1) throws -> A1 (NSData+Extension.swift:0)
9 MyAppName 0x00079a70 (extension in MyAppName):__ObjC.NSData.dataType.getter : Swift.String? (NSData+Extension.swift:55)
10 MyAppName 0x00079948 @objc (extension in MyAppName):__ObjC.NSData.dataType.getter : Swift.String? (NSData+Extension.swift:0)
11 MyAppName 0x000d2264 MyAppName.DataManager.(fetchImagesFromServer (MyAppName.ImageSet) -> ()).(closure #1) (DataManager.swift:1214)
12 libdispatch.dylib 0x218cd823 _dispatch_call_block_and_release + 8
13 libdispatch.dylib 0x218dc5e9 _dispatch_root_queue_drain + 1558
14 libdispatch.dylib 0x218dbfcd _dispatch_worker_thread3 + 94
15 libsystem_pthread.dylib 0x21a91b29 _pthread_wqthread + 1022
16 libsystem_pthread.dylib 0x21a91718 start_wqthread + 6
更新
以下代码变体仍然会发生崩溃:
// Get first byte
var c = UnsafeMutablePointer<UInt8>.alloc(1)
defer { c.dealloc(1) }
self.getBytes(c, length: 1)
switch (c[0]) { ...
Swift 数组更像是 C++ std::vector
而不是 C 数组:它除了数组元素之外还有其他内容。您无法使用 &c
获取指向第一个元素的指针。您需要向数组询问指向其元素的指针,如下所示:
var c = [UInt8](count: 1, repeatedValue: 0)
c.withUnsafeMutableBufferPointer { buffer in
getBytes(buffer.baseAddress, length: 1)
}
不过,对于您的特定情况,使用数组似乎有点过分了。为什么不这样做:
var c: UInt8 = 0;
self.getBytes(&c, length: 1)
在 Apple 工程师的帮助下(通过 TSI 票证),问题终于被确定了。
以上用于读取第一个字节的所有代码排列均有效且有效。
问题是 NSData
对象是在使用 Parse iOS SDK 从服务器获取文件时创建的,该文件将数据存储在具有文件保护密钥 NSFileProtectionCompleteUntilFirstUserAuthentication
的临时文件中.
文件保护密钥仅允许用户在重启后解锁一次设备后读取NSData
对象的数据。虽然解锁前数据不可读,但是可以创建NSData
对象,甚至可以访问NSData.length
属性。但是,尝试读取数据会引发异常。
我更改了代码并在尝试使用 UIApplication.sharedApplication().protectedDataAvailable
读取受保护数据之前添加了检查是否可用。
您可能想知道为什么在设备未解锁之前应用程序已获取文件。该应用程序由远程用户通知启动。这就解释了为什么崩溃很少发生。
学到了 2 件事:
- 始终检查您的文件保护密钥
- 苹果技术支持讲解超深入,物超所值
扩展NSData判断文件类型:
extension NSData {
var dataType: String? {
// Ensure data length is at least 1 byte
guard self.length > 0 else { return nil }
// Get first byte
var c = [UInt8](count: 1, repeatedValue: 0)
self.getBytes(&c, length: 1)
// Identify data type
switch (c[0]) {
case 0xFF:
return "jpg"
case 0x89:
return "png"
case 0x47:
return "gif"
case 0x49, 0x4D:
return "tiff"
default:
return nil //unknown
}
}
}
在从服务器获取的图像数据的 NSData
对象上调用上述方法。
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
do {
// Fetch image synchronously from server
let query = PFQuery(className: <...>)
let result = try query.getFirstObject()
guard
let imageObject = result.objectForKey(<...>) as? PFFile,
let imageData = try? imageObject.getData(),
let image = imageData.dataType == "gif" ? UIImage.animatedImageWithAnimatedGIFData(imageData) : UIImage(data: imageData)
else {
return
}
<...>
} catch (let error as NSError) {
<...>
}
}
然而,应用程序很少在第 self.getBytes
行崩溃:
这是什么原因?
getBytes 的缓冲区是 &c
,一个 UnsafeMutablePointer
- 因此我是否必须考虑任何特殊的内存?
更新
以下代码变体仍然会发生崩溃:
// Get first byte
var c: UInt8 = 0;
self.getBytes(&c, length: 1)
更新
以下代码变体仍然会发生崩溃:
// Get first byte
var c = [UInt8](count: 1, repeatedValue: 0)
c.withUnsafeMutableBufferPointer {
buffer in
getBytes(buffer.baseAddress, length: 1)
}
guard c.indices.contains(0) else { return nil }
我遇到了以下崩溃并包含了整个线程,也许有人能发现提示:
Thread 18 Crashed:
0 libsystem_platform.dylib 0x21a8e198 _platform_memmove$VARIANT$CortexA9 + 92
1 Foundation 0x22512923 __34-[_NSDispatchData getBytes:range:]_block_invoke + 176
2 libdispatch.dylib 0x218d238d _dispatch_data_apply + 82
3 libdispatch.dylib 0x218d4a51 dispatch_data_apply + 26
4 Foundation 0x22512865 -[_NSDispatchData getBytes:range:] + 86
5 Foundation 0x2267730b -[_NSDispatchData getBytes:length:] + 24
6 MyAppName 0x00079ba0 partial apply forwarder for (extension in MyAppName):__ObjC.NSData.(dataType.getter : Swift.String?).(closure #1) (NSData+Extension.swift:54)
7 MyAppName 0x00079c14 partial apply forwarder for reabstraction thunk helper from @callee_owned (@inout Swift.UnsafeMutableBufferPointer<Swift.UInt8>) -> (@unowned (), @error @owned Swift.ErrorType) to @callee_owned (@inout Swift.UnsafeMutableBufferPointer<Swift.UInt8>) -> (@out (), @error @owned Swift.ErrorType) (NSData+Extension.swift:0)
8 MyAppName 0x00079cb8 generic specialization <Swift.UInt8, ()> of Swift.Array.withUnsafeMutableBufferPointer <A> ((inout Swift.UnsafeMutableBufferPointer<A>) throws -> A1) throws -> A1 (NSData+Extension.swift:0)
9 MyAppName 0x00079a70 (extension in MyAppName):__ObjC.NSData.dataType.getter : Swift.String? (NSData+Extension.swift:55)
10 MyAppName 0x00079948 @objc (extension in MyAppName):__ObjC.NSData.dataType.getter : Swift.String? (NSData+Extension.swift:0)
11 MyAppName 0x000d2264 MyAppName.DataManager.(fetchImagesFromServer (MyAppName.ImageSet) -> ()).(closure #1) (DataManager.swift:1214)
12 libdispatch.dylib 0x218cd823 _dispatch_call_block_and_release + 8
13 libdispatch.dylib 0x218dc5e9 _dispatch_root_queue_drain + 1558
14 libdispatch.dylib 0x218dbfcd _dispatch_worker_thread3 + 94
15 libsystem_pthread.dylib 0x21a91b29 _pthread_wqthread + 1022
16 libsystem_pthread.dylib 0x21a91718 start_wqthread + 6
更新
以下代码变体仍然会发生崩溃:
// Get first byte
var c = UnsafeMutablePointer<UInt8>.alloc(1)
defer { c.dealloc(1) }
self.getBytes(c, length: 1)
switch (c[0]) { ...
Swift 数组更像是 C++ std::vector
而不是 C 数组:它除了数组元素之外还有其他内容。您无法使用 &c
获取指向第一个元素的指针。您需要向数组询问指向其元素的指针,如下所示:
var c = [UInt8](count: 1, repeatedValue: 0)
c.withUnsafeMutableBufferPointer { buffer in
getBytes(buffer.baseAddress, length: 1)
}
不过,对于您的特定情况,使用数组似乎有点过分了。为什么不这样做:
var c: UInt8 = 0;
self.getBytes(&c, length: 1)
在 Apple 工程师的帮助下(通过 TSI 票证),问题终于被确定了。 以上用于读取第一个字节的所有代码排列均有效且有效。
问题是 NSData
对象是在使用 Parse iOS SDK 从服务器获取文件时创建的,该文件将数据存储在具有文件保护密钥 NSFileProtectionCompleteUntilFirstUserAuthentication
的临时文件中.
文件保护密钥仅允许用户在重启后解锁一次设备后读取NSData
对象的数据。虽然解锁前数据不可读,但是可以创建NSData
对象,甚至可以访问NSData.length
属性。但是,尝试读取数据会引发异常。
我更改了代码并在尝试使用 UIApplication.sharedApplication().protectedDataAvailable
读取受保护数据之前添加了检查是否可用。
您可能想知道为什么在设备未解锁之前应用程序已获取文件。该应用程序由远程用户通知启动。这就解释了为什么崩溃很少发生。
学到了 2 件事:
- 始终检查您的文件保护密钥
- 苹果技术支持讲解超深入,物超所值