如何通过 Swift 中的闭包设置惰性计算属性的值?

Posted

技术标签:

【中文标题】如何通过 Swift 中的闭包设置惰性计算属性的值?【英文标题】:How to set the value of lazy computed property via a closure in Swift? 【发布时间】:2019-06-17 15:47:28 【问题描述】:

所以我在这个问题上纠结了一段时间,在网上找不到解决我特定问题的问题。

我正在尝试在description 中设置值,该值被定义为惰性计算属性并利用自执行闭包。

为了获得书的描述,我进行了一个 API 调用,将另一个处理程序传递给 API 完成处理程序,以便我可以在惰性计算属性中设置书的描述。

我知道我下面的代码是错误的,因为我得到了错误:

无法将“()”类型的值转换为指定的“字符串”类型

class Book : NSObject 
    func getInfo(for name: String, handler: @escaping (_ string: String) -> String) 
        let task = URLSession.shared.dataTask(with: "foo_book.com" + name)  (data, response, error) in
            guard let data = data else return
            descriptionStr = String(data: data, encoding: .utf8) ?? "No description found"
            handler(descriptionStr)
        
    

    lazy var description: String = 
        getInfo(for: self.name)  str in
            return str
        
    ()

如何设置description的值?

我尝试了两种方法。使用 while 循环等待 boolean: 不优雅并且违背了异步的目的。在 description 中使用临时变量 - 不起作用,因为 getInfo 在 API 调用完成之前返回。

如果您想知道我的用例:我想在表格视图中将书籍显示为单独的视图,但我不想在打开表格视图时为每本书进行 api 调用。因此,我想懒惰地进行 API 调用。由于描述应该是不变的,因此我选择将其设为惰性计算属性,因为它只会计算一次。

编辑:对于那些想知道的人,我的解决方案是下面提到的 cmets。我的方法不正确 - 我没有尝试异步设置属性,而是创建了一个方法并在视图控制器中获取描述。

【问题讨论】:

术语:这不是计算属性,而是存储属性。所有计算的属性都是“惰性的”。语义:你不能让一个属性在 Swift 中异步返回一个值。 How to properly declare a computed property, when calculation uses background threads?的可能重复 另见:***.com/questions/25203556/…,尤其是 Rob Napier 的回答 您遇到了该错误,因为getInfo 返回的内容什么都不是(这就是错误中出现“()”的原因)而不是字符串。如果你想通过调用你的方法为description返回一些东西,这需要返回String 【参考方案1】:

cmets 中的解释已经足以说明问题所在,我将在您的用例中添加解决方案。

我想在表格视图中将书籍显示为单独的视图,但我 当我打开表格视图时,不想为每本书进行 api 调用。 因此,我想懒惰地进行 API 调用。

首先,在这里设置lazy 是否有意义。以后无论何时您调用描述,您都会保留URLSession 的参考,并且您将为所有书籍执行此操作。看起来你很容易造成内存泄漏。

第二,task.resume()getInfo方法中的必填项。

第三,你的模型(书)不应该提出请求。为什么?想一想,我在上面给出了一个原因。异步确实意味着并行,所有这些网络调用都在队列中,如果您有太多模型,则事件循环中的网络调用过多。

您可以将网络调用责任转移到服务可能是BookService,然后有这样的方法BookService.getInfo(_ by: name)。你的 Book 模型应该是一个愚蠢的类。

  class Book 
     let description: String

     init(desc: String) 
         self.description = desc
     
  

现在您的控制器/交互器将负责调用服务以获取信息。在这里做懒惰的调用。

     class BookTableViewController: ViewController 

        init(bookService: BookService, book: [String]) 
        

        # you can call when you want to show this book
        func loadBook(_ name: String) -> Book 

           BookService.getInfo(name).map  Book(desc: str) 
        

        func tableView(UITableView, didSelectRowAt: IndexPath) 
               let bookName = ....
               # This is lazy loading
               let book = loadBook(bookName)
               showThisBook()
        
     

在这里,您可以对 loadBook 进行惰性调用。希望这会有所帮助。

【讨论】:

至少,让我知道为什么它被否决了;这样我就可以改进了。

以上是关于如何通过 Swift 中的闭包设置惰性计算属性的值?的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的惰性属性相当于 Objective C 中的惰性 Init getter

如何在 Swift 中将计算的闭包属性转换为闭包?

Swift 中的可选闭包属性

swift之属性

多次加载的 Swift 惰性变量(计算属性?)

Swift 中的惰性只读属性