ios - ZIPFoundation:通过数据提供程序将大 PNG 写入存档时出现问题

标签 ios swift zipfoundation

更新:我可以通过将 PNG 大小设置为超过任意值(即 700 x 700 pt 失败)来重现这一点。在任意值下读写正常。我不确定那条线的确切位置。

我使用 zip 存档作为我的文档文件格式。尝试从文件浏览器页面的多个存档中读取 PNG 预览时,我看到了意外结果。

后台查询文档URL,然后创建文件数据对象。查询完成后,将在主线程上调用 UI 更新,文件数据对象充当 Collection View 的数据提供者。

PNG 像这样序列化为数据:

let imageData = UIImagePNGRepresentation(image)

当读取数据时,相关条目被提取到内存中,然后反序列化到它们各自的对象中。

我看到的是文档缩略图只是间歇性地显示。 ZIPFoundation 是否有可能对于后台操作不是线程安全的?

我已经在一个更简单的测试项目中对此进行了模拟,它运行良好,但我没有阅读多个文件,也没有在后台调用它。我做错了什么(下面的代码)吗?

消费者闭包是否使用自己的线程(也许我在它完成之前返回数据)?

do {
    let decoder = JSONDecoder()
    let archive = Archive(url: URL, accessMode: .read)

    // Metadata
    if let metadataData = try archive?.readData(named: Document.MetadataFilename) {
        self.metadata = try decoder.decode(Metadata.self, from: metadataData)
    } else {
        logDebug(self, "metadata not read")
    }

    // Preview
    if let previewData = try archive?.readData(named: Document.PreviewFilename) {
        if let image = UIImage(data: previewData) {
            self.image = image
            return
        }
    } else {
        logDebug(self, "image not read")
    }

} catch {
    logError(self, "Loading of FileWrapper failed with error: \(error.localizedDescription)")
}

// Failure fall through
// Mark this as failed by using the x image
self.image = UIImage(named: "unavailable")
}

为了方便,我对 Archive 的扩展:

/// Associates optional data with entry name
struct NamedData {
    let name : String
    let data : Data?
}

// MARK: - Private
extension Archive {

    private func addData(_ entry: NamedData) throws {
        let archive = self
        let name = entry.name
        let data = entry.data
        do {
            if let data = data {
                try archive.addEntry(with: name, type: .file, uncompressedSize: UInt32(data.count), compressionMethod: .none, provider: { (position, size) -> Data in
                    return data
                })
            }
        } catch {
            throw error
        }
    }

    private func removeData(_ entry: NamedData) throws {
        let archive = self
        let name = entry.name
        do {
            if let entry = archive[name] { try archive.remove(entry) }
        } catch {
            throw error
        }
    }
}

// MARK: - Public
extension Archive {

    /// Update NamedData entries in the archive
    func updateData(entries: [NamedData]) throws {
        // Walk each entry and overwrite
        do {
            for entry in entries {
                try removeData(entry)
                try addData(entry)
            }
        } catch {
            throw error
        }
    }

    /// Add NamedData entries to the archive (updateData is the preferred
    /// method since no harm comes from removing entries before adding them)
    func addData(entries: [NamedData]) throws {
        // Walk each entry and create
        do {
            for entry in entries {
                try addData(entry)
            }
        } catch {
            throw error
        }
    }

    /// Read Data out of the entry using its name
    func readData(named name: String) throws -> Data? {
        let archive = self
        // Get data from entry
        do {
            var entryData : Data? = nil
            if let entry = archive[name] {
                // _ = returned checksum
                let _ = try archive.extract(entry, consumer: { (data) in
                    entryData = data
                })
            }
            return entryData
        } catch {
            throw error
        }
    }
}

最佳答案

ZIPFoundation 中基于闭包的 API 旨在提供/使用分 block 数据。根据数据的最终大小和配置的 block 大小(可选参数,默认为 16*1024),可以多次调用提供者/消费者闭包。

当您通过

提取条目时
let _ = try archive.extract(entry, consumer: { (data) in
    entryData = data
})

你总是用 consumer 闭包提供的最新 block 覆盖 entryData(如果最终大小大于 block 大小)。

相反你可以使用

var entryData = Data()
let _ = try archive.extract(entry, consumer: { (data) in
    entryData.append(data)
})

确保整个条目都累积在 entryData 对象中。

您的 Provider 代码中也发生了类似的事情。不要总是返回整个图像数据对象,您应该在每次调用闭包时提供一个 block (从 position 开始,size)。

关于ios - ZIPFoundation:通过数据提供程序将大 PNG 写入存档时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50124304/

相关文章:

ios - 如何替换 NSMutableAttributedString 中的字符串?

ios - 如何在不在 zip 目录中创建目录的情况下压缩文件?

ios - 使用单个推送动画推送和呈现 View Controller

ios - 在不同的显示器上 react native 相同大小的圆圈

iOS 套接字连接崩溃

ios - 在 Swift 中,如何强制键盘在程序启动时打开?

ios - 如何以编程方式触发 UILocalNotification?

ios - 使用迦太基在 Xcode 中导入 ZipFoundation 不起作用

ios - 加载 600 行后 UITableview 分页崩溃应用程序