Как установить значение ленивого вычисляемого свойства через замыкание в Swift? - PullRequest
0 голосов
/ 24 января 2019

Так что я застрял на этой проблеме какое-то время и не могу найти вопросы, касающиеся моей конкретной проблемы в Интернете.

Я пытаюсь установить значение в description, которое определяется как ленивое вычисляемое свойство и использует самоисполняющееся замыкание.

Чтобы получить описание книги, я делаю вызов API, передавая другой обработчик обработчику завершения API, чтобы я мог задать описание книги внутри ленивого вычисляемого свойства.

Я знаю, что мой код неверен, так как я получаю ошибку:

Невозможно преобразовать значение типа '()' в указанный тип 'String'

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 для ожидания логического значения: inelegant и поражения цели асинхронности. Использование временной переменной внутри description - не работает, потому что getInfo возвращается до завершения вызова API.

В случае, если вам интересен мой вариант использования: я хочу отображать книги в виде отдельных представлений в табличном представлении, но не хочу делать вызовы API для каждой книги, когда открываю табличное представление. Таким образом, я хочу лениво сделать вызов API. Поскольку описания должны быть инвариантными, я решил сделать его вычисляемым свойством с отложенным вычислением, поскольку оно будет вычислено только один раз.

Редактировать: Для тех, кто задается вопросом, мое решение было в виде комментариев, упомянутых ниже. Мой подход был неправильным - вместо того, чтобы пытаться установить свойство асинхронно, я создал метод и получил описание в контроллере представления.

1 Ответ

0 голосов
/ 24 января 2019

Уже объяснений в комментариях достаточно для того, что идет не так, я просто добавлю решение для вашего варианта использования.

Я хочу отображать книги как отдельные представления в виде таблицы, но я я не хочу делать вызовы API для каждой книги, когда я открываю таблицу. Таким образом, я хочу лениво сделать вызов API.

Прежде всего, имеет смысл делать lazy здесь. Всякий раз, когда в будущем вы будете называть описание, вы сохраняете ссылку на URLSession, и вы сделаете это для всех книг. Похоже, вы легко создадите утечку памяти.

Во-вторых, task.resume() требуется в методе getInfo.

В-третьих, ваша модель (Книга) не должна делать запрос. Зачем? думаю, я привел одну причину выше. Асинхронизация означает параллель, все эти сетевые вызовы находятся в очереди. Если у вас много моделей, слишком много сетевых вызовов в цикле событий.

Вы можете перенести ответственность за сетевые вызовы на обслуживание, например BookService, и затем использовать метод, подобный этому BookService.getInfo(_ by: name). Ваша книга модель должна быть тупой класс.

  class Book {
     let description: String

     init(desc: String) {
         self.description = desc
     }
  }

Теперь ваш контроллер / Interactor позаботится о том, чтобы позвонить в службу для получения информации. Сделайте ленивый звонок здесь.

     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. Надеюсь, это поможет.

...