Запретить принудительное необязательное развертывание после инициализации объекта

Мне было интересно, можно ли удалить принудительное развертывание (Swift) при использовании такого кода:

var currentProductRequest : SKProductsRequest?

public func requestProducts() -> Bool {
    currentProductRequest = SKProductsRequest(productIdentifiers: Set<String>(identifiers))
    currentProductRequest!.delegate = self
    currentProductRequest!.start()
}

В этом случае currentProductRequest будет 100% не равным нулю; однако это необязательно, и в приведенном выше контексте компилятор не знает, что он не будет равен нулю, и ему необходимо принудительно развернуть. Поскольку инициализация SKProductsRequest никогда не приведет к нулю, я не могу использовать

if let request = SKProductsRequest(productIdentifiers: Set<String>(identifiers)) {
    ....
}

Один из известных мне способов - сделать объявление currentProductRequest неявно развернутым, например var currentProductRequest: SKProductsRequest !. Однако это заставит компилятор думать, что currentProductRequest никогда не будет равен нулю, что может быть не так.

Я знаю, что принудительное развертывание никогда не вызовет проблем (в данном случае), но мне это кажется некрасивым. Кто-нибудь знает, есть ли способ сообщить компилятору, что currentProductRequest не будет равен нулю и, таким образом, удалит принудительное развертывание.


person Guus    schedule 03.08.2016    source источник


Ответы (3)


В подобных случаях я предпочитаю определять локальную константу с тем же именем и назначать ее свойству с помощью ключевого слова self, как мы это делаем в инициализаторах:

var currentProductRequest : SKProductsRequest?

public func requestProducts() -> Bool {
    let currentProductRequest = SKProductsRequest(productIdentifiers: Set<String>(identifiers))
    self.currentProductRequest = currentProductRequest
    currentProductRequest.delegate = self
    currentProductRequest.start()
    return true
}
person Shadow Of    schedule 03.08.2016
comment
Спасибо за ответ, это решение уже было опубликовано Abizern (не считая перегрузки имени и использования self.). Но все же спасибо! - person Guus; 03.08.2016

Обычно я делаю это, создавая необязательный объект, настраиваю его и затем устанавливаю для него параметр:

var currentProductRequest : SKProductsRequest?

public func requestProducts() -> Bool {
    let productRequest = SKProductsRequest(productIdentifiers: Set<String>(identifiers))
    productRequest.delegate = self
    productRequest.start()
    currentProductRequest = productRequest
}

Отредактировано для добавления

Есть еще один способ сделать это, который немного более надуманный: создать общедоступную переменную, которая прозрачно разворачивает необязательное и вместо этого возвращает его, чтобы вам не приходилось иметь дело с необязательным:

private var _currentProductRequest : SKProductsRequest?

public var currentProductRequest: SKProductsRequest = {
    return _currentProductRequest!
}()

public func requestProducts() -> Bool {
    let productRequest = SKProductsRequest(productIdentifiers: Set<String>(identifiers))
    productRequest.delegate = self
    productRequest.start()
    _currentProductRequest = productRequest
}

Но это, наверное, перебор.

person Abizern    schedule 03.08.2016
comment
Это хороший обходной путь. Если нет другого синтаксиса, разработанного для того, что я просил, я обязательно воспользуюсь этим подходом. - person Guus; 03.08.2016

Если ваш identifiers не является дополнительным, вы можете воспользоваться lazy var:

lazy var currentProductRequest: SKProductsRequest = {
        let productRequest = SKProductsRequest(productIdentifiers: Set<String>(identifiers))
        productRequest.delegate = self
        return productRequest
    }()

public func requestProducts() -> Bool {
    currentProductRequest!.start()
}
person Oleg Danu    schedule 03.08.2016
comment
Это хорошее решение, однако мои идентификаторы изменяемы (по замыслу). Однако это решение многих других случаев :) - person Guus; 03.08.2016