Как использовать функцию Object-C с блоком в Swift4 способом @noescap? - PullRequest
0 голосов
/ 29 января 2019

Я разрабатываю приложение для iOS на Swift4 с платформой Object-C под названием «YapDatabase».Существует функция Object-C с таким блоком в классе 'YapDatabaseConnection':

- (void)readWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block;

Я использую функцию следующим образом:

static func readNovelIds() -> [String]? {        
    let account = XFAccountManager.share().account
    var events: [XFNovelClickEvent]?
    OTRDatabaseManager.shared.readOnlyDatabaseConnection?.read({ (transaction) in
        events = XFNovelClickEvent.allNovelClickEvents(accountId: account.uniqueId, transaction: transaction)
    })
    guard let clickEvents = events else {
        return nil
    }
    let readNovelsIds = clickEvents.map {
        $0.bookId ?? ""
    }
    return readNovelsIds
}

Я думал, что закрытие будетвыполняется сразу после того, как объявлен параметр 'events'.На самом деле замыкание не выполняется до возврата результата.Чтобы найти причину, я открываю файл с именем 'YapDatabaseConnection.h (Interface)', сгенерированный Xcode (с cmd + shift + o), и обнаружил, что функция была переведена в Swift следующим образом:

open func read(_ block: @escaping (YapDatabaseReadTransaction) -> Void)

Итак, как мне использовать эту функцию @noescap?

Ответы [ 3 ]

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

Причина, по которой Xcode установил мост для блока target-c как @escaping, заключается в том, что блок может быть выполнен после возврата из функции.

Поскольку у вас нет YapDatabase, вы не можете изменить исходный код, чтобы сделать его не экранированным, поэтому вы можете сделать так, чтобы ваша readNovelIds функция приняла замыкание в качестве параметра и передала возвратзначение через закрытие.

static func readNovelIds(resultHandler: @escaping ([String]?) -> ()) {
    let account = XFAccountManager.share().account
    var events: [XFNovelClickEvent]?
    OTRDatabaseManager.shared.readOnlyDatabaseConnection?.read({ (transaction) in
        events = XFNovelClickEvent.allNovelClickEvents(accountId: account.uniqueId, transaction: transaction)
        if let clickEvents = events {
            let readNovelsIds = clickEvents.map {
                $0.bookId ?? ""
            }
            resultHandler(readNovelsIds)
        }
        resultHandler(nil)
    })
}
0 голосов
/ 30 января 2019

Если метод на самом деле является синхронным (то есть он не позволяет блоку выходить из своего контекста), метод заголовка Objective C должен быть украшен NS_NOESCAPE.Глядя на документацию (которая говорит о том, что она является синхронной), и на реализацию, она должна быть аннотирована таким образом.

- (void)readWithBlock:(void (NS_NOESCAPE ^)(YapDatabaseReadTransaction *transaction))block;

Это, я полагаю, должно позволить импортеру интерфейса Swift добавить объявление @noescaping,Вероятно, вам следует отправить запрос об ошибке в проект YapDatabase;они могут изменить это там.

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

Как вызывающая сторона, вы не можете измениться при закрытии.Это до функции read().Если вы контролируете эту функцию, вам нужно изменить ее, чтобы немедленно вызвать замыкание.Если вы не контролируете его, то не можете изменить его поведение.

Асинхронный вызов можно преобразовать в синхронный вызов, используя DispatchGroup, как описано в Ожидание завершения задачи.Однако вы не можете сделать вызов базы данных в главной очереди;вы рискуете сломать приложение.Как правило, в этом случае вы должны просто использовать асинхронные вызовы (т.е. сделать readNovelIds также асинхронным и принять обработчик завершения).

...