iOS开发进阶之列表加载图片

本文介绍了在iOS开发中如何优化列表加载大量图片的性能,包括使用复用机制、网络图片的异步加载、图片解码延迟、CATiledLayer的局部渲染以及NSCache的缓存策略,以提升用户体验并有效管理内存。
摘要由CSDN通过智能技术生成

iOS开发进阶之列表加载图片

列表加载图片通常使用UITableViewUICollectionView,由于列表中内容数量不确定并且对于图片质量要求也不确定,所以对于图片加载的优化是很有必要的。
iOS开发进阶之列表加载图片
首先借鉴前文,我们逐步进行操作,以下是加载1000张图片的列表。

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1000
    }
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UICollectionViewCell", for: indexPath)
        cell.contentView.layer.contents = UIImage(named: "iOS")?.cgImage
        return cell
    }

实验结果
可以看到在使用复用机制后,我们的内存占用还比较低,我们接下来换网络图片。
对应代码调整,以下。

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UICollectionViewCell", for: indexPath)
        let vi = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: 150, height: 100)))
        vi.kf.setImage(with: URL(string: "https://img0.baidu.com/it/u=245753553,2056265008&fm=253&fmt=auto?w=1280&h=800")!)
        cell.contentView.addSubview(vi)
        return cell
    }

实验结果
此时的网络图片对应尺寸会比较大,在滚动时可以明显感受到卡顿,接下来换一下加载方式,以下。

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UICollectionViewCell", for: indexPath)
        DispatchQueue.global(qos: .background).async {
            let url = "https://img0.baidu.com/it/u=245753553,2056265008&fm=253&fmt=auto?w=1280&h=800"
            ImageDownloader.default.downloadImage(with: URL(string: url)!, options: .none) { result in
                switch result
                {
                case let .success(value):
                    UIGraphicsBeginImageContextWithOptions(CGSize(width: 150, height: 100), true, 0)
                    value.image.draw(in: CGRect(origin: .zero, size: CGSize(width: 150, height: 100)))
                    let image = UIGraphicsGetImageFromCurrentImageContext()
                    UIGraphicsEndImageContext()
                    DispatchQueue.main.async {
                        cell.contentView.layer.contents = image?.cgImage
                    }
                default:
                    break
                }
                
            }
        }
        
        return cell
    }

以上使用了自己的方式进行图片解码,之所以如此是因为在如果直接加载图片在iOS中就会强制图片解码,相对而言图片解码相当耗时,现在我们将图片解码放在异步延迟处理,然后等解码完成后再进行加载,这样的好处就是不会阻塞主线程。
实验结果
如果我们加载的图片更大,超过了一整屏幕,这时候我们可以使用CATiledLayer,这是因为它做到了异步的按块的渲染。
先来看看它的注释,以下。

/* This is a subclass of CALayer providing a way to asynchronously
 * provide tiles of the layer's content, potentially cached at multiple
 * levels of detail.
 *
 * As more data is required by the renderer, the layer's
 * -drawInContext: method is called on one or more background threads
 * to supply the drawing operations to fill in one tile of data. The
 * clip bounds and CTM of the drawing context can be used to determine
 * the bounds and resolution of the tile being requested.
 *
 * Regions of the layer may be invalidated using the usual
 * -setNeedsDisplayInRect: method. However update will be asynchronous,
 * i.e. the next display update will most likely not contain the
 * changes, but a future update will.
 *
 * Note: do not attempt to directly modify the `contents' property of
 * an CATiledLayer object - doing so will effectively turn it into a
 * regular CALayer. */

@available(iOS 2.0, *)
open class CATiledLayer : CALayer
-drawInContext: method is called on one or more background threads to supply the drawing operations to fill in one tile of data.

可以在多个后台线程上调用进行绘图。

-setNeedsDisplayInRect: method. However update will be asynchronous,  i.e. the next display update will most likely not contain the changes, but a future update will.

不仅异步还可以做到局部渲染。
那么除此之外,还有其他的手段进行优化吗?
我们来看看NSCache,以下。

@available(iOS 4.0, *)
open class NSCache<KeyType, ObjectType> : NSObject where KeyType : AnyObject, ObjectType : AnyObject {

    
    open var name: String

    
    unowned(unsafe) open var delegate: (any NSCacheDelegate)?

    
    open func object(forKey key: KeyType) -> ObjectType?

    open func setObject(_ obj: ObjectType, forKey key: KeyType) // 0 cost

    open func setObject(_ obj: ObjectType, forKey key: KeyType, cost g: Int)

    open func removeObject(forKey key: KeyType)

    
    open func removeAllObjects()

    
    open var totalCostLimit: Int // limits are imprecise/not strict

    open var countLimit: Int // limits are imprecise/not strict

    open var evictsObjectsWithDiscardedContent: Bool
}

@available(*, unavailable)
extension NSCache : @unchecked Sendable {
}

public protocol NSCacheDelegate : NSObjectProtocol {

    
    @available(iOS 4.0, *)
    optional func cache(_ cache: NSCache<AnyObject, AnyObject>, willEvictObject obj: Any)
}

还可以将图片加入缓存,然后在需要使用的时候直接从缓存中取出,NSCache可以自动进行缓存管理,它做了func didReceiveMemoryWarning()的应对处理。
图片的加载优化目的是利用更多空闲资源和时间,在激活时更迅速的执行加载过程,比如在列表停止滚动时或者RunLoop休眠时异步加载,异步对图片解码,利用好缓存,在内存警告时及时释放内存,最后渲染时直接渲染。

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kevin写代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值