- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
iOS 10 即将发布,因此值得测试应用程序与它的兼容性。在这样的测试中,我们发现我们的应用无法在 iOS10 上恢复后台下载。在旧版本上运行良好的代码不适用于新版本,无论是在模拟器上还是在设备上。
我没有将我们的代码减少到最小的工作测试用例,而是在互联网上搜索 NSUrlSession 教程并对其进行了测试。行为是相同的:继续在 iOS 的旧版本上工作,但在 10th 上中断。
下载已恢复。您可以检查它如何与 iOS10 之前的版本一起使用。
下载失败。在 xcode 控制台中你可以看到:
2016-09-02 16:11:24.913 HalfTunes[35205:2279228] *** -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL
2016-09-02 16:11:24.913 HalfTunes[35205:2279228] *** -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL
2016-09-02 16:11:24.913 HalfTunes[35205:2279228] Invalid resume data for background download. Background downloads must use http or https and must download to an accessible file.
如果您在下载文件时激活离线模式,您将获得
Url session completed with error: Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL} {
NSLocalizedDescription = "unsupported URL";
}
当网络关闭时,下载永远不会在网络再次启动时恢复。其他暂停用例(例如重启)也不起作用。
我尝试使用中建议的代码检查返回的 resumeData 是否有效
How can I check that an NSData blob is valid as resumeData for an NSURLSessionDownloadTask?
但目标文件已经到位。尽管 resumeData 格式已更改,现在文件名存储在 NSURLSessionResumeInfoTempFileName 中,您必须将 NSTemporaryDirectory() 附加到它。
除此之外,我已经向苹果公司提交了一份错误报告,但他们还没有回复。
NSUrlSession 的恢复是否在所有其他应用程序中中断?应用端可以解决吗?
最佳答案
这个问题是由于 currentRequest 和 originalRequest NSKeyArchived 用一个不寻常的“NSKeyedArchiveRootObjectKey”根编码而不是 NSKeyedArchiveRootObjectKey 常量(字面意思是“根”)和 NSURL(Mutable)Request 编码过程中的一些其他行为不当引起的。
我在 beta 1 中检测到并提交了一个错误(编号 27144153,以防你想要复制)。我什至给 NSURLSession 团队的支持人员“Quinn the Eskimo”(apple dot com 的 eskimo1)发送了一封电子邮件,以确认他们收到了邮件,他说他们收到了并意识到了问题。
更新:我终于想出了解决这个问题的方法。将数据提供给 correctResumeData() 函数,它将返回可用的简历数据
更新 2:您可以使用 NSURLSession.correctedDownloadTaskWithResumeData()/URLSession.correctedDownloadTask(withResumeData:) 函数获取具有正确 originalRequest 和 currentRequest 变量的任务
更新 3: Quinn 说此问题已在 iOS 10.2 中解决,您可以继续使用此代码以与 iOS 10.0 和 10.1 兼容,并且它可以毫无问题地与新版本一起使用。
(对于 Swift 3 代码,请在下方滚动,对于 Objective C,请参见 leavesstar post 但我没有测试它)
swift 2.3:
func correctRequestData(data: NSData?) -> NSData? {
guard let data = data else {
return nil
}
// return the same data if it's correct
if NSKeyedUnarchiver.unarchiveObjectWithData(data) != nil {
return data
}
guard let archive = (try? NSPropertyListSerialization.propertyListWithData(data, options: [.MutableContainersAndLeaves], format: nil)) as? NSMutableDictionary else {
return nil
}
// Rectify weird __nsurlrequest_proto_props objects to $number pattern
var k = 0
while archive["$objects"]?[1].objectForKey("$\(k)") != nil {
k += 1
}
var i = 0
while archive["$objects"]?[1].objectForKey("__nsurlrequest_proto_prop_obj_\(i)") != nil {
let arr = archive["$objects"] as? NSMutableArray
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_prop_obj_\(i)"] {
dic.setObject(obj, forKey: "$\(i + k)")
dic.removeObjectForKey("__nsurlrequest_proto_prop_obj_\(i)")
arr?[1] = dic
archive["$objects"] = arr
}
i += 1
}
if archive["$objects"]?[1].objectForKey("__nsurlrequest_proto_props") != nil {
let arr = archive["$objects"] as? NSMutableArray
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_props"] {
dic.setObject(obj, forKey: "$\(i + k)")
dic.removeObjectForKey("__nsurlrequest_proto_props")
arr?[1] = dic
archive["$objects"] = arr
}
}
// Rectify weird "NSKeyedArchiveRootObjectKey" top key to NSKeyedArchiveRootObjectKey = "root"
if archive["$top"]?.objectForKey("NSKeyedArchiveRootObjectKey") != nil {
archive["$top"]?.setObject(archive["$top"]?["NSKeyedArchiveRootObjectKey"], forKey: NSKeyedArchiveRootObjectKey)
archive["$top"]?.removeObjectForKey("NSKeyedArchiveRootObjectKey")
}
// Reencode archived object
let result = try? NSPropertyListSerialization.dataWithPropertyList(archive, format: NSPropertyListFormat.BinaryFormat_v1_0, options: NSPropertyListWriteOptions())
return result
}
func getResumeDictionary(data: NSData) -> NSMutableDictionary? {
var iresumeDictionary: NSMutableDictionary? = nil
// In beta versions, resumeData is NSKeyedArchive encoded instead of plist
if #available(iOS 10.0, OSX 10.12, *) {
var root : AnyObject? = nil
let keyedUnarchiver = NSKeyedUnarchiver(forReadingWithData: data)
do {
root = try keyedUnarchiver.decodeTopLevelObjectForKey("NSKeyedArchiveRootObjectKey") ?? nil
if root == nil {
root = try keyedUnarchiver.decodeTopLevelObjectForKey(NSKeyedArchiveRootObjectKey)
}
} catch {}
keyedUnarchiver.finishDecoding()
iresumeDictionary = root as? NSMutableDictionary
}
if iresumeDictionary == nil {
do {
iresumeDictionary = try NSPropertyListSerialization.propertyListWithData(data, options: [.MutableContainersAndLeaves], format: nil) as? NSMutableDictionary;
} catch {}
}
return iresumeDictionary
}
func correctResumeData(data: NSData?) -> NSData? {
let kResumeCurrentRequest = "NSURLSessionResumeCurrentRequest"
let kResumeOriginalRequest = "NSURLSessionResumeOriginalRequest"
guard let data = data, let resumeDictionary = getResumeDictionary(data) else {
return nil
}
resumeDictionary[kResumeCurrentRequest] = correctRequestData(resumeDictionary[kResumeCurrentRequest] as? NSData)
resumeDictionary[kResumeOriginalRequest] = correctRequestData(resumeDictionary[kResumeOriginalRequest] as? NSData)
let result = try? NSPropertyListSerialization.dataWithPropertyList(resumeDictionary, format: NSPropertyListFormat.XMLFormat_v1_0, options: NSPropertyListWriteOptions())
return result
}
extension NSURLSession {
func correctedDownloadTaskWithResumeData(resumeData: NSData) -> NSURLSessionDownloadTask {
let kResumeCurrentRequest = "NSURLSessionResumeCurrentRequest"
let kResumeOriginalRequest = "NSURLSessionResumeOriginalRequest"
let cData = correctResumeData(resumeData) ?? resumeData
let task = self.downloadTaskWithResumeData(cData)
// a compensation for inability to set task requests in CFNetwork.
// While you still get -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL error,
// this section will set them to real objects
if let resumeDic = getResumeDictionary(cData) {
if task.originalRequest == nil, let originalReqData = resumeDic[kResumeOriginalRequest] as? NSData, let originalRequest = NSKeyedUnarchiver.unarchiveObjectWithData(originalReqData) as? NSURLRequest {
task.setValue(originalRequest, forKey: "originalRequest")
}
if task.currentRequest == nil, let currentReqData = resumeDic[kResumeCurrentRequest] as? NSData, let currentRequest = NSKeyedUnarchiver.unarchiveObjectWithData(currentReqData) as? NSURLRequest {
task.setValue(currentRequest, forKey: "currentRequest")
}
}
return task
}
}
swift 3:
func correct(requestData data: Data?) -> Data? {
guard let data = data else {
return nil
}
if NSKeyedUnarchiver.unarchiveObject(with: data) != nil {
return data
}
guard let archive = (try? PropertyListSerialization.propertyList(from: data, options: [.mutableContainersAndLeaves], format: nil)) as? NSMutableDictionary else {
return nil
}
// Rectify weird __nsurlrequest_proto_props objects to $number pattern
var k = 0
while ((archive["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "$\(k)") != nil {
k += 1
}
var i = 0
while ((archive["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "__nsurlrequest_proto_prop_obj_\(i)") != nil {
let arr = archive["$objects"] as? NSMutableArray
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_prop_obj_\(i)"] {
dic.setObject(obj, forKey: "$\(i + k)" as NSString)
dic.removeObject(forKey: "__nsurlrequest_proto_prop_obj_\(i)")
arr?[1] = dic
archive["$objects"] = arr
}
i += 1
}
if ((archive["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "__nsurlrequest_proto_props") != nil {
let arr = archive["$objects"] as? NSMutableArray
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_props"] {
dic.setObject(obj, forKey: "$\(i + k)" as NSString)
dic.removeObject(forKey: "__nsurlrequest_proto_props")
arr?[1] = dic
archive["$objects"] = arr
}
}
/* I think we have no reason to keep this section in effect
for item in (archive["$objects"] as? NSMutableArray) ?? [] {
if let cls = item as? NSMutableDictionary, cls["$classname"] as? NSString == "NSURLRequest" {
cls["$classname"] = NSString(string: "NSMutableURLRequest")
(cls["$classes"] as? NSMutableArray)?.insert(NSString(string: "NSMutableURLRequest"), at: 0)
}
}*/
// Rectify weird "NSKeyedArchiveRootObjectKey" top key to NSKeyedArchiveRootObjectKey = "root"
if let obj = (archive["$top"] as? NSMutableDictionary)?.object(forKey: "NSKeyedArchiveRootObjectKey") as AnyObject? {
(archive["$top"] as? NSMutableDictionary)?.setObject(obj, forKey: NSKeyedArchiveRootObjectKey as NSString)
(archive["$top"] as? NSMutableDictionary)?.removeObject(forKey: "NSKeyedArchiveRootObjectKey")
}
// Reencode archived object
let result = try? PropertyListSerialization.data(fromPropertyList: archive, format: PropertyListSerialization.PropertyListFormat.binary, options: PropertyListSerialization.WriteOptions())
return result
}
func getResumeDictionary(_ data: Data) -> NSMutableDictionary? {
// In beta versions, resumeData is NSKeyedArchive encoded instead of plist
var iresumeDictionary: NSMutableDictionary? = nil
if #available(iOS 10.0, OSX 10.12, *) {
var root : AnyObject? = nil
let keyedUnarchiver = NSKeyedUnarchiver(forReadingWith: data)
do {
root = try keyedUnarchiver.decodeTopLevelObject(forKey: "NSKeyedArchiveRootObjectKey") ?? nil
if root == nil {
root = try keyedUnarchiver.decodeTopLevelObject(forKey: NSKeyedArchiveRootObjectKey)
}
} catch {}
keyedUnarchiver.finishDecoding()
iresumeDictionary = root as? NSMutableDictionary
}
if iresumeDictionary == nil {
do {
iresumeDictionary = try PropertyListSerialization.propertyList(from: data, options: PropertyListSerialization.ReadOptions(), format: nil) as? NSMutableDictionary;
} catch {}
}
return iresumeDictionary
}
func correctResumeData(_ data: Data?) -> Data? {
let kResumeCurrentRequest = "NSURLSessionResumeCurrentRequest"
let kResumeOriginalRequest = "NSURLSessionResumeOriginalRequest"
guard let data = data, let resumeDictionary = getResumeDictionary(data) else {
return nil
}
resumeDictionary[kResumeCurrentRequest] = correct(requestData: resumeDictionary[kResumeCurrentRequest] as? Data)
resumeDictionary[kResumeOriginalRequest] = correct(requestData: resumeDictionary[kResumeOriginalRequest] as? Data)
let result = try? PropertyListSerialization.data(fromPropertyList: resumeDictionary, format: PropertyListSerialization.PropertyListFormat.xml, options: PropertyListSerialization.WriteOptions())
return result
}
extension URLSession {
func correctedDownloadTask(withResumeData resumeData: Data) -> URLSessionDownloadTask {
let kResumeCurrentRequest = "NSURLSessionResumeCurrentRequest"
let kResumeOriginalRequest = "NSURLSessionResumeOriginalRequest"
let cData = correctResumeData(resumeData) ?? resumeData
let task = self.downloadTask(withResumeData: cData)
// a compensation for inability to set task requests in CFNetwork.
// While you still get -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL error,
// this section will set them to real objects
if let resumeDic = getResumeDictionary(cData) {
if task.originalRequest == nil, let originalReqData = resumeDic[kResumeOriginalRequest] as? Data, let originalRequest = NSKeyedUnarchiver.unarchiveObject(with: originalReqData) as? NSURLRequest {
task.setValue(originalRequest, forKey: "originalRequest")
}
if task.currentRequest == nil, let currentReqData = resumeDic[kResumeCurrentRequest] as? Data, let currentRequest = NSKeyedUnarchiver.unarchiveObject(with: currentReqData) as? NSURLRequest {
task.setValue(currentRequest, forKey: "currentRequest")
}
}
return task
}
}
关于ios - 在 iOS10 上恢复 NSUrlSession,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39346231/
IO 设备如何知道属于它的内存中的值在memory mapped IO 中发生了变化? ? 例如,假设内存地址 0 专用于保存 VGA 设备的背景颜色。当我们更改 memory[0] 中的值时,VGA
我目前正在开发一个使用Facebook sdk登录(通过FBLoginView)的iOS应用。 一切正常,除了那些拥有较旧版本的facebook的人。 当他们按下“使用Facebook登录”按钮时,他
假设我有: this - is an - example - with some - dashesNSRange将使用`rangeOfString:@“-”拾取“-”的第一个实例,但是如果我只想要最后
Card.io SDK提供以下详细信息: 卡号,有效期,月份,年份,CVV和邮政编码。 如何从此SDK获取国家名称。 - (void)userDidProvideCreditCardInfo:(Car
iOS 应用程序如何从网络服务下载图片并在安装过程中将它们安装到用户的 iOS 设备上?可能吗? 最佳答案 您无法控制应用在用户设备上的安装,因此无法在安装过程中下载其他数据。 只需在安装后首次启动应
我曾经开发过一款企业版 iOS 产品,我们公司曾将其出售给大型企业,供他们的员工使用。 该应用程序通过 AppStore 提供,企业用户获得了公司特定的配置文件(包含应用程序配置文件)以启用他们有权使
我正在尝试将 Card.io SDK 集成到我的 iOS 应用程序中。我想为 CardIO ui 做一个简单的本地化,如更改取消按钮标题或“在此保留信用卡”提示文本。 我在 github 上找到了这个
我正在使用 CardIOView 和 CardIOViewDelegate 类,没有可以设置为 YES 的 BOOL 来扫描 collectCardholderName。我可以看到它在 CardIOP
我有一个集成了通话工具包的 voip 应用程序。每次我从我的 voip 应用程序调用时,都会在 native 电话应用程序中创建一个新的最近通话记录。我在 voip 应用程序中也有自定义联系人(电话应
iOS 应用程序如何知道应用程序打开时屏幕上是否已经有键盘?应用程序运行后,它可以接收键盘显示/隐藏通知。但是,如果应用程序在分屏模式下作为辅助应用程序打开,而主应用程序已经显示键盘,则辅助应用程序不
我在模拟器中收到以下错误: ImageIO: CGImageReadSessionGetCachedImageBlockData *** CGImageReadSessionGetCachedIm
如 Apple 文档所示,可以通过 EAAccessory Framework 与经过认证的配件(由 Apple 认证)进行通信。但是我有点困惑,因为一些帖子告诉我它也可以通过 CoreBluetoo
尽管现在的调试器已经很不错了,但有时找出应用程序中正在发生的事情的最好方法仍然是古老的 NSLog。当您连接到计算机时,这样做很容易; Xcode 会帮助弹出日志查看器面板,然后就可以了。当您不在办公
在我的 iOS 应用程序中,我定义了一些兴趣点。其中一些有一个 Kontakt.io 信标的名称,它绑定(bind)到一个特定的 PoI(我的意思是通常贴在信标标签上的名称)。现在我想在附近发现信标,
我正在为警报提示创建一个 trigger.io 插件。尝试从警报提示返回数据。这是我的代码: // Prompt + (void)show_prompt:(ForgeTask*)task{
您好,我是 Apple iOS 的新手。我阅读并搜索了很多关于推送通知的文章,但我没有发现任何关于 APNS 从 io4 到 ios 6 的新更新的信息。任何人都可以向我提供 APNS 如何在 ios
UITabBar 的高度似乎在 iOS 7 和 8/9/10/11 之间发生了变化。我发布这个问题是为了让其他人轻松找到答案。 那么:在 iPhone 和 iPad 上的 iOS 8/9/10/11
我想我可以针对不同的 iOS 版本使用不同的 Storyboard。 由于 UI 的差异,我将创建下一个 Storyboard: Main_iPhone.storyboard Main_iPad.st
我正在写一些东西,我将使用设备的 iTunes 库中的一部分音轨来覆盖 2 个视频的组合,例如: AVMutableComposition* mixComposition = [[AVMutableC
我创建了一个简单的 iOS 程序,可以顺利编译并在 iPad 模拟器上运行良好。当我告诉 XCode 4 使用我连接的 iPad 设备时,无法编译相同的程序。问题似乎是当我尝试使用附加的 iPad 时
我是一名优秀的程序员,十分优秀!