问题
App runs fine on every device that qualifies for deployment target of 9.3+ except iPad2. The url is good. Works on every iPhone and every other iPad. The crash is on a physical iPad2 and Simulator iPad2 iOS 9.3.
doc.save(to: target, for: .forCreating, completionHandler: {(success) in
if (success) {
print("Save succeeded")
}
} else {
print("Save failed")
}
})
This is where it crashes. Get to a breakpoint at this line, and do not get to breakpoints in completion handler or either print. Again, just the one model of just iPad.
The crash log is over my head. Does it make sense to any of you? Thank you.
Edit: expanded the crash log
回答1:
tl;dr: It's a bug in the Swift Data type. Solution: In your UIDocument contents(forType:)
implementation, change this:
return data
to this:
return NSData(data:data)
More info: Note that merely casting data
to NSData won't help:
return data as NSData
That doesn't get us any further because it's the bridging that's the problem. We're already bridging to NSData and it isn't helping. You have to create a completely new object that is not a Swift Data object.
Even more info: For future generations who come along and want to test this, the crash can be reliably reproduced as follows. Make a new Single View project, with an app delegate and a ViewController as usual. In the ViewController.swift file, put this:
import UIKit
class WhatsUpDoc: UIDocument {
var array = [String]()
override func load(fromContents contents: Any, ofType typeName: String?) throws {}
override func contents(forType typeName: String) throws -> Any {
let data = NSKeyedArchiver.archivedData(withRootObject: array)
return data // comment out this line to avoid the crash
return NSData(data:data)
}
}
class ViewController: UIViewController {
var doc : WhatsUpDoc?
override func viewDidLoad() {
super.viewDidLoad()
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let baseURL = documentsDirectory.appendingPathComponent("Untitled")
let doc = WhatsUpDoc(fileURL: baseURL)
self.doc = doc
self.doc!.save(to:self.doc!.fileURL, for: .forCreating)
}
}
Configure your project to have a deployment target of 9.0, and make sure you've got a 9.0 simulator SDK on hand. In Window > Devices, give yourself an iPad 2 simulator. Set that simulator as the project's destination. Run, and crash. Comment out the line that says to comment it out, and don't crash.
Post-analysis Q&A:
Wait, so what exactly _is_ the bug? You're not saying you can't write a Swift Data object to disk, are you?
No, it's got something to do with the peculiar threaded nature of writing during a UIDocument file save. Note that we are crashing in a background thread (thread 10 or 11 in the OP's screen shots). And what we are crashing in is a method of SwiftNSData
, the NSData wrapped by a Swift Data. We're trying to enumerate the data's bytes on this background thread, and we can't; presumably this isn't thread-safe on certain device types, i.e. 32-bit devices. My solution is to get Swift Data out of the picture completely.
Okay, so it turns out this bug is known; the answer is given here: https://stackoverflow.com/a/41163395/341994 How about that?
Okay, but I figured it out independently in response to the current question. I didn't find out about the other question and answer until later, when it occurred to me to do a search. So I've marked the current question as a duplicate, but I'm leaving my answer for historical purposes, esp. as it gives a clear test case (which the other question does not).
By the way, I do not know why using NSMutableData(data:)
would be better than my solution of using NSData(data:)
, but that's what Apple says to do, so let's just take it as gospel.
来源:https://stackoverflow.com/questions/42443976/what-does-this-swift-ipad-crash-log-mean