存在的基础:在阅读之前,了解您不能通过image
keypath 将 UIImage 分配给图像视图插座的属性会有所帮助\UIImageView.image
。这是财产:
@IBOutlet weak var iv: UIImageView!
现在,这会编译吗?
let im = UIImage()
let kp = \UIImageView.image
self.iv[keyPath:kp] = im // error
不!
可选类型“UIImage?”的值 必须解包为“UIImage”类型的值
好的,现在我们已经为实际用例做好了准备。
我实际上想了解的是 Combine 框架.assign
订阅者如何在幕后工作。为了进行实验,我尝试使用自己的 Assign 对象。在我的示例中,我的发布者管道生成一个 UIImage 对象,并将其分配给image
UIImageView property 的属性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
那里指定密钥路径?关于关键路径如何与可选属性一起工作似乎有些技巧,但我不知道它是什么。
在 Martin RI 的一些输入之后意识到,如果我们pub
显式键入作为生成,我们将获得与添加将 UIImage 包装在 Optional 中的UIImage?
效果相同的效果。map
所以这编译和工作
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