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

时间:2019-01-24 00:19:59

标签: swift asynchronous closures

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

我正在尝试在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:inelegant并破坏了异步的目的。在description中使用临时变量-不起作用,因为getInfo在API调用完成之前返回。

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

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

1 个答案:

答案 0 :(得分:-1)

注释中的解释已经足够解决发生了什么问题,我将为您的用例添加解决方案。

  

我想将图书显示为表格视图中的单个视图,但是我   当我打开tableview时,不想为每本书进行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进行延迟调用。希望这会有所帮助。