ios - 如何使用 Swift 在后台线程中保存文件?

标签 ios swift multithreading

我正在尝试制作一个使用 Web 服务从目录中获取一些数据的应用程序,但我还需要将数据保存到设备中,包括图像,为此我正在使用 Alamofire AlamofireImage 框架,用于使用网络服务。我使用 Realm 框架将生成的对象保存在数据库中,对于图像,我将 UIImage 保存到一个文件中。

基本上,ViewController 有一个显示数据的 tableView,但由于图像写入文件,它看起来很慢。

这是我的写作功能:

func saveImage(_ image: UIImage) {
    if let data = UIImagePNGRepresentation(image) {
        let name = "images/person_directory_\(id).png"
        let docsDir = getDocumentsDirectory()
        let filename = docsDir.appendingPathComponent(name)
        let fm = FileManager.default

        if !fm.fileExists(atPath: docsDir.appendingPathComponent("images").path) {
            do {
                try fm.createDirectory(at: docsDir.appendingPathComponent("images"), withIntermediateDirectories: true, attributes: nil)
                try data.write(to: filename)
                try! realm?.write {
                    self.imageLocal = name
                }
            }
            catch {
                print(error)
            }
        }
        else {
            do {
                try data.write(to: filename, options: .atomic)
                try! realm?.write {
                    self.imageLocal = name
                }
            }
            catch {
                print(error)
            }

        }
    }
}

我在Alamofire下载图片的时候调用了这个函数

if person.imageLocal != nil,  let image = person.loadLocalImage() {
        print("Load form disk: \(person.imageLocal)")
        cell.imgProfile.image = image
    }
    else if !(person.image?.isEmpty)! {
        Alamofire.request(person.image!).responseImage(completionHandler: { (response) in
            if response.result.isSuccess {
                if let image = response.result.value {
                    person.saveImage(image)
                    cell.imgProfile.image = image
                    print("Downloaded: \(person.imageLocal)")
                }
            }
        })
    }

但是 tableView 在滚动时看起来很慢,我试图将写入操作放到不同的线程中,这样它就可以在不影响应用程序性能的情况下通过使用 DispatchQeue

进行写入
DispatchQueue.global(qos: .background).async {
                    do {
                        try data.write(to: filename)
                    }
                    catch {
                        print(error)
                    }
                }

但即便如此,应用程序仍然滞后。

更新:

我按照 Rob 的建议尝试了这个:

func saveImage(_ image: UIImage) {
    if let data = UIImagePNGRepresentation(image) {
        let name = "images/person_directory_\(id).png"
        do {
            try realm?.write {
                self.imageLocal = name
            }
        }
        catch {
            print("Realm error")
        }
        DispatchQueue.global().async {
            let docsDir = self.getDocumentsDirectory()
            let filename = docsDir.appendingPathComponent(name)
            let fm = FileManager.default

            if !fm.fileExists(atPath: docsDir.appendingPathComponent("images").path) {
                do {
                    try fm.createDirectory(at: docsDir.appendingPathComponent("images"), withIntermediateDirectories: true, attributes: nil)
                }
                catch {
                    print(error)
                }
            }
            do {
                try data.write(to: filename)
            }
            catch {
                print(error)
            }
        }
    }
}

我无法调度 Realm 编写,因为 Realm 不支持多线程。 它仍然滚动滞后,但没有第一次那么多。

最佳答案

所以@Rob 给出的正确答案是

   DispatchQueue.global().async {
      do {
         try data.write(to: filename)
      }
      catch {
         print(error)
      }
    }

但同样重要的是永远不要使用异步调用中对 UITableViewCell 的引用(再次归功于@Rob)。例如,从代码的异步部分设置单元格值。

                cell.imgProfile.image = image

UITableViewCells 被重新使用,所以你不知道原来的单元格还在同一个索引处使用。为了进行测试,请非常快速地滚动您的列表以便加载图像,如果您发现图像出现在错误的单元格中,那就是重用问题。

因此,您需要从异步回调中确定新图像的单元格索引是否可见,然后获取该索引的当前单元格以设置它的图像。如果索引不可见,则存储/缓存图像,直到它的索引滚动到 View 中。那时它的单元格将使用 UITableView.cellForRowAtIndexPath 创建,您可以在那里设置图像。

关于ios - 如何使用 Swift 在后台线程中保存文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42514554/

相关文章:

ios - UISearchBar 放大镜图标位置改变

ios - 在后台线程中执行组合 future 不起作用

ios - NSDateFormatter 仍在解析而不是格式不正确

python - 使用Python函数作为类

java - 特定线程数

c++ - 无法将接口(interface)从主线程编码到工作线程

ios - iPhone6、iPhone 6加: What naming convention the new images have to follow?

ios - 由于条件绑定(bind)错误的初始化程序,无法将文本 append 到字符串数组

ios - 根据单击的单元格更改变量

ios - 在 Swift 中从 Realm 加载图像路径的问题