Mutable Property:在值访问中执行方法



我正在使用reactiveswift sdwebimage下载/cache useravatars的API,然后在我的ViewControllers中显示它们。

我有多个要显示用户的viewControllers,然后他们收听其异步加载。

我实现下面描述的流程的最佳方法是什么?

我想在这里创建的流程是:

  1. ViewControllerA想要访问用户avatar
  2. 这是第一次访问用户,然后做一个API请求
  3. ViewControllerA lisgens for UserAvatar信号
  4. ViewControllerA暂时显示占位符
  5. ViewControllerB想要访问用户avatar
  6. ViewControllerB聆听用户的信号
  7. ViewControllerB暂时显示占位符
  8. 用户的API请求已完成,然后发送一个由ViewControllers观察到的信号
  9. viewControllers用新鲜图像为其UIImageView刷新

这是我的实际代码:

class ViewControllerA {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // ... Cell creation
        // type(of: user) == User.self (see class User below)
        user.loadAvatarImage()
        disposable = user.image.producer
            .observe(on: UIScheduler())
            .startWithValues { image in
                // image is is either a placeholder or the real avatar
                cell.userImage.image = image
        }
    }
}
class ViewControllerB {
    override func viewDidLoad() {
        super.viewDidLoad()
        // type(of: user) == User.self (see class User below)
        user.loadAvatarImage()
        disposable = user.image.producer
            .observe(on: UIScheduler())
            .startWithValues { image in
                // image is is either a placeholder or the real avatar
                headerImageView.image = image
        }
    }
}
class User: Mappable {
    // ... User implementation
    let avatarImage = MutableProperty<UIImage?>(nil)
    // To call before accessing avatarImage.value
    func loadAvatarImage() {
        getAvatar { image in
            self.avatarImageProperty.value = image
        }
    }
    private func getAvatar(completion: @escaping ((UIImage) -> Void)) {
        // ... Async image download
        competion(image)
    }
}

我在收听信号之前没有发现呼叫user.loadAvatarImage()很干净...

我知道我的代码不是那么"反应性",我仍然有反应性概念。随时批评,我正在尝试改善自己

事先感谢您的建议。

处理这种情况的最佳方法是创建一个SignalProducer

  1. 如果启动SignalProducer时已经下载了image:立即发射.value(image),其次是.completed

  2. 如果启动SignalProducerimage当前正在下载:image下载完成后,EMITS .value(image)随后是.completed

  3. 如果尚未下载image,并且在启动SignalProducer时尚未下载:启动image的启动下载,image完成下载时,下载了EMITS .value(image) .completed

reactiveswift为我们提供了一个"手动"构造函数,用于信号生产者,使我们能够编写每次启动信号生产者时运行的命令式代码:

private let image = MutableProperty<UIImage?>(.none)
private var imageDownloadStarted = false
public func avatarImageSignalProducer() -> SignalProducer<UIImage, NoError> {
  return SignalProducer { observer, lifetime in
    //if image download hasn't started, start it now
    if (!self.imageDownloadStarted) {
      self.imageDownloadStarted = true
      self.getAvatar { self.image = $0 }
    }
    //emit .value(image) followed by .completed when the image has downloaded, or immediately if it has already downloaded
    self.image.producer //use our MutableProperty to get a signalproducer for the image download
      .skipNil() //dont send the nil value while we wait for image to download
      .take(first: 1) //send .completed after image value is sent
      .startWithSignal { $0.observe(observer) } //propogate these self.image events to the avatarImageSignalProducer
  }
}

要使您的代码更加"反应性",您可以使用eactivevecocoa库将avatarImageSignalProducer绑定到UI:

reactivecocoa不带BindingTarget用于UIImageView.image,因此我们自己写一个扩展名:

import ReactiveCocoa
extension Reactive where Base: UIImageView {
  public var image: BindingTarget<UIImage> {
    return makeBindingTarget { $0.image = $1 }
  }
}

这使我们可以使用ViewController中的eactivecocoa绑定操作员在viewDidLoad/cellForRowAtIndexPath/等中清理我们的代码:

class ViewControllerA {
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // ... Cell creation
    cell.userImage <~ user.avatarImageSignalProducer()
      .take(until: cell.reactive.prepareForReuse) //stop listening to signal & free memory when cell is reused before image loads
  }
}
class ViewControllerB {
  override func viewDidLoad() {
    headerImageView.image <~ user.avatarImageSignalProducer()
      .take(during: self.reactive.lifetime) //stop listening to signal & free memory when VC is deallocated before image loads
  }
}

考虑记忆&amp;将数据绑定到内存中未引用的UI时的周期性引用(例如,如果我们的用户是一个全局变量,则在VC被交易之后留在内存中,而不是VC的属性(。在这种情况下,我们必须明确停止在交易VC时聆听信号,否则将永远不会释放其内存。上面代码中对.take(until: cell.reactive.prepareForReuse).take(during: self.reactive.lifetime)的调用都是明确停止信号以进行内存管理目的的示例。

相关内容

  • 没有找到相关文章

最新更新