You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift MVC架构下是否允许将Model类传入自定义UITableViewCell视图?

问题答复

现有设计是否符合MVC规范?

完全不可接受,主要有两个层面的问题:

  • 架构职责混乱:MVC架构中视图层(包括UITableViewCell)的唯一职责是接收数据、渲染展示,不应该持有Model层对象,更不应该自行处理网络请求、缓存管理的业务逻辑。
  • 实际功能存在严重bug:你当前在每个DataViewCell内部都初始化了独立的Model实例,各个实例的imagecache字典完全隔离,缓存根本不会共享,滑动列表时同一张图片会重复下载多次,完全达不到你做缓存的预期。另外Cell复用过程中,旧的网络请求回调会覆盖当前Cell的图片,会出现图片错乱的问题。

可采用的优化方案

第一步:调整缓存的存储逻辑

把图片缓存从Model类中抽离,做成全局共享的NSCache单例,保证全局只有一份缓存,所有图片加载逻辑都访问同一个缓存对象即可。
示例缓存单例写法:

class ImageCacheManager {
    static let shared = ImageCacheManager()
    private let cache = NSCache<NSString, UIImage>()
    
    private init() {}
    
    func getImage(for url: String) -> UIImage? {
        return cache.object(forKey: url as NSString)
    }
    
    func saveImage(_ image: UIImage, for url: String) {
        cache.setObject(image, forKey: url as NSString)
    }
}

第二步:把图片加载逻辑从Cell中剥离

两种常用方案,都可以避免Cell感知Model层:

方案1:在ViewController层处理图片加载

在cellForRowAt方法中处理图片的缓存查询、网络请求,请求成功后给对应Cell的图片控件赋值,同时做好Cell复用的校验,避免图片错乱:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: Constants.ID, for: indexPath) as! DataViewCell
    let item = data[indexPath.row]
    // 先赋值文本
    cell.dataName.text = item.strdata
    cell.dataImage.image = nil // 先清空复用的旧图片
    guard let imgUrl = item.thumbnail else { return cell }
    // 查缓存
    if let cachedImg = ImageCacheManager.shared.getImage(for: imgUrl) {
        cell.dataImage.image = cachedImg
        return cell
    }
    // 没有缓存就请求
    guard let url = URL(string: imgUrl) else { return cell }
    URLSession.shared.dataTask(with: url) { [weak cell, weak self] data, _, error in
        guard let data = data, error == nil, let image = UIImage(data: data), let self = self else { return }
        // 存缓存
        ImageCacheManager.shared.saveImage(image, for: imgUrl)
        DispatchQueue.main.async {
            // 校验当前Cell对应的url是否还是当前请求的url,避免复用错乱
            if self.data[indexPath.row].thumbnail == imgUrl {
                cell?.dataImage.image = image
            }
        }
    }.resume()
    return cell
}

对应的DataViewCell可以删掉model属性、图片加载逻辑,只保留基础的UI控件声明即可。

方案2:封装UIImageView扩展(更推荐)

把带缓存的图片加载逻辑封装成UIImageView的扩展,Cell只需要调用扩展方法,不需要感知任何业务逻辑,代码更简洁,复用性更高:

extension UIImageView {
    func setImage(with urlStr: String, placeholder: UIImage? = nil) {
        self.image = placeholder
        guard let url = URL(string: urlStr) else { return }
        // 查缓存
        if let cachedImg = ImageCacheManager.shared.getImage(for: urlStr) {
            self.image = cachedImg
            return
        }
        // 发起请求
        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let data = data, error == nil, let image = UIImage(data: data) else { return }
            ImageCacheManager.shared.saveImage(image, for: urlStr)
            DispatchQueue.main.async {
                self?.image = image
            }
        }.resume()
    }
}

调用的时候只需要在Cell的setCell方法里直接调用即可:

func setCell(_ data: DataItem) {
    self.dataName.text = data.strdata
    self.dataImage.setImage(with: data.thumbnail ?? "")
}

内容的提问来源于stack exchange,提问作者mendokusai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 04:00:03