Основание существования: перед чтением будет полезно знать, что вы не можете назначить UIImage свойству image
выхода просмотра изображений с помощью ключевого пути \UIImageView.image
. Вот свойство:
@IBOutlet weak var iv: UIImageView!
Теперь это будет компилироваться?
let im = UIImage()
let kp = \UIImageView.image
self.iv[keyPath:kp] = im // error
No!
Значение необязательного типа «UIImage?» необходимо развернуть до значения типа 'UIImage'
Хорошо, теперь мы готовы к фактическому использованию.
На самом деле я пытаюсь понять, как подписчик платформы Combine .assign
работает за кулисами. Чтобы поэкспериментировать, я попробовал использовать свой собственный объект Assign. В моем примере конвейер издателя создает объект UIImage, и я назначаю его свойству image
свойства UIImageView self.iv
.
Если мы используем метод .assign
, он компилируется и работает:
URLSession.shared.dataTaskPublisher(for: url)
.map {$0.data}
.replaceError(with: Data())
.compactMap { UIImage(data:$0) }
.receive(on: DispatchQueue.main)
.assign(to: \.image, on: self.iv)
.store(in:&self.storage)
Итак, говорю я себе, чтобы увидеть, как это работает, я удалю .assign
и заменю его своим собственным объектом Assign:
let pub = URLSession.shared.dataTaskPublisher(for: url)
.map {$0.data}
.replaceError(with: Data())
.compactMap { UIImage(data:$0) }
.receive(on: DispatchQueue.main)
let assign = Subscribers.Assign(object: self.iv, keyPath: \UIImageView.image)
pub.subscribe(assign) // error
// (and we will then wrap in AnyCancellable and store)
Блап! Мы не можем этого сделать, потому что UIImageView.image
- это необязательный UIImage, а мой издатель создает простой и простой UIImage.
Я попытался обойти это, развернув Optional в ключевом пути:
let assign = Subscribers.Assign(object: self.iv, keyPath: \UIImageView.image!)
pub.subscribe(assign)
Круто, что компилируется. Но он вылетает во время выполнения, предположительно из-за того, что изображение представления изображения изначально nil
.
Теперь я могу нормально обойти все это, добавив map
к моему конвейеру, который объединяет UIImage в Optional, чтобы все типы соответствовали правильно. Но у меня вопрос: как это на самом деле работает? Я имею в виду, почему бы мне не сделать это в первом коде, где я использую .assign
? Почему я могу указать здесь путь к клавишам .image
? Кажется, есть некоторая хитрость в том, как ключевые пути работают с дополнительными свойствами, но я не знаю, что это такое.
После некоторого ввода от Мартина Р. я понял, что если мы введем pub
явно как производящий UIImage?
, мы получим тот же эффект, что и добавление map
, которое обертывает UIImage в Optional. Итак, это компилируется и работает
let pub : AnyPublisher<UIImage?,Never> = URLSession.shared.dataTaskPublisher(for: url)
.map {$0.data}
.replaceError(with: Data())
.compactMap { UIImage(data:$0) }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
let assign = Subscribers.Assign(object: self.iv, keyPath: \UIImageView.image)
pub.subscribe(assign)
let any = AnyCancellable(assign)
any.store(in:&self.storage)
Это все еще не объясняет, как работает оригинальный .assign
. Похоже, что он может протолкнуть опциональность типа вверх по конвейеру в оператор .receive
. Но я не понимаю, как это возможно.
self.iv.image
равен нулю? - person Martin R   schedule 08.01.2020.store
AnyCancellable, как и следовало ожидать, и код загружается и показывает изображение очень хорошо.) Вот почему я сбит с толку; Я не обнаруживал проблемы, пока не попытался разобраться в том, что.assign
делает за кулисами, предоставив моего собственного подписчика Assign, подписавшись на него и продвигая его на AnyCancellable (не показано). - person matt   schedule 08.01.2020let pub = ...
- этоPublishers.ReceiveOn<Publishers.CompactMap<Publishers.ReplaceError<Publishers.Map<URLSession.DataTaskPublisher, Data>>, UIImage>, DispatchQueue>
. Если вы аннотируете его явно, но с заменойUIImage
наUIImage?
, ошибка исчезнет. Похоже, что это то, что компилятор делает в первом примере из.assign(to: \.image, on: self.iv)
, посколькуself.iv.image
является необязательным. - person Martin R   schedule 08.01.2020compactMap
наmap
- имеет ли это смысл? Я не уверен. - person Martin R   schedule 08.01.2020receiveOn
, ее результат будет набран как UIImage. Если вы добавитеassign
,receiveOn
вывод будет набран как UIImage ?. Это как если быassign
мог подтолкнуть опциональность вверх по конвейеру. Я не понимаю, как это происходит. - person matt   schedule 08.01.2020eraseToAnyPublisher
и явно введемpub
generic как параметризующий UIImage? вся проблема уходит. Я добавлю это к вопросу. - person matt   schedule 08.01.2020