Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
FtnApp 的缩略图实践
Search
Frank Xu
August 26, 2012
Programming
0
50
FtnApp 的缩略图实践
Frank Xu
August 26, 2012
Tweet
Share
More Decks by Frank Xu
See All by Frank Xu
Proxy Pattern
yyfrankyy
0
85
微信读书阅读器架构
yyfrankyy
2
2.2k
Watcher - EventBus Reinvented
yyfrankyy
0
150
SQLite 调优实践
yyfrankyy
0
130
JSDoc 的使用
yyfrankyy
0
140
交易平台化(前端)
yyfrankyy
0
170
淘宝搜索前端优化
yyfrankyy
0
170
淘宝排行榜 V3 项目总结
yyfrankyy
0
120
Other Decks in Programming
See All in Programming
チームで開発し事業を加速するための"良い"設計の考え方 @ サポーターズCoLab 2025-07-08
agatan
1
420
Azure AI Foundryではじめてのマルチエージェントワークフロー
seosoft
0
170
A full stack side project webapp all in Kotlin (KotlinConf 2025)
dankim
0
120
チームのテスト力を総合的に鍛えて品質、スピード、レジリエンスを共立させる/Testing approach that improves quality, speed, and resilience
goyoki
5
870
Webの外へ飛び出せ NativePHPが切り拓くPHPの未来
takuyakatsusa
2
550
効率的な開発手段として VRTを活用する
ishkawa
0
140
すべてのコンテキストを、 ユーザー価値に変える
applism118
3
1.3k
プロダクト志向ってなんなんだろうね
righttouch
PRO
0
190
技術同人誌をMCP Serverにしてみた
74th
1
640
PicoRuby on Rails
makicamel
2
130
AIともっと楽するE2Eテスト
myohei
6
2.6k
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
510
Featured
See All Featured
The Cost Of JavaScript in 2023
addyosmani
51
8.5k
Site-Speed That Sticks
csswizardry
10
690
Become a Pro
speakerdeck
PRO
29
5.4k
Adopting Sorbet at Scale
ufuk
77
9.5k
Agile that works and the tools we love
rasmusluckow
329
21k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
281
13k
Facilitating Awesome Meetings
lara
54
6.4k
Navigating Team Friction
lara
187
15k
GitHub's CSS Performance
jonrohan
1031
460k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
331
22k
Making Projects Easy
brettharned
116
6.3k
For a Future-Friendly Web
brad_frost
179
9.8k
Transcript
FtnApp 的缩略图实践 AyangXu
列表缩略图和预览大图 图片缓存策略(磁盘,内存) 耗时操作的应对(读写、压缩) 大图滑动的逐步调优 Agenda
两种缩略图的异同
列表缩略图 文件小(15-20k) 文件多(列表) 滑动的时候频繁读 高缓存命中
预览大图 文件大(200k+) 绘制耗时长 重用率低 内存控制要求更高
共同特点 平滑性要求高 多重检查:本地 ➜ 原图(压缩) ➜ 网络 过度状态(ICON / loadingView)
small loadThumbnail big QMCache thumb.db file original file web thumbnail
小图的缓存策略(磁盘) small loadThumbnail QMCache thumb.db original file web thumbnail
小图的缓存策略(磁盘) 全部小图保存到⼀一个 DB 启动的时候子线程加载(15k*100张) 滚动停止时,只更新 visibleCells 先更新内存,显示,再在后面更新 DB 不要在 cellForRowAtIndexPath
里做太多事情
图片缓存策略
小图的缓存策略(内存) 内存更新策略 缩略图只加不减 全部小图,包括默认的 ICON 都只读⼀一次
大图的缓存策略(内存) NSCache NSDiscardableContent / NSPurgeableData 不Copy Key,释放时机不可控 QMCache NSMutableArray w/
NSMutableDictionary 自定义 Capacity,多了就删
耗时操作的应对
QMCache thumb.db file original file web thumbnail 难点: 两种 IO
操作 两个网络操作 相互嵌套 本地 ➜ 原图 ➜ 网络
GCD VS OperationQueue GCD 保留作用域,线程池 不好控制并发任务,不好取消 NSOperationQueue w/ NSBlockOperation FIFO,封装了
GCD,可配置并发数,支持取消 保留还在队列里的 URL,避免重复的操作 快速滑动时,取消未开始的任务,优先留给当前页
[queue addOperation: [NSBlockOperation blockOperationWithBlock:^{ dispatch_async( dispatch_get_main_queue(), ^{/*UI*/}); }]]; dispatch_async( dispatch_get_global_queue(0,0),
^{ dispatch_async( dispatch_get_main_queue(),^{/*UI*/}); }); VS
大图滑动的逐步调优
大图滑动的基本实现
两层UIScrollView 1 2
⼀一次性准备ContentSize { numOfImages * (width + padding), height }
[previewImage renderMultipleImages: ^(int page, downloadImageCallback callback) { asyncLoadImage(page, ^(UIImage *image)
{ callback(image); }); } ]; 将读取逻辑扩展出去
评估工具 Core Animations Allocations Instruments
大图滑动 V1 图片和 loadingView 分开两个数组 未渲染部分用 [NSNull null] 占位,提前创建 好N个
loadingView 检查是否 [NSNull class] 来判定是否需要加 载 scrollViewDidEndDecelerating 时加载 每次加载时创建 view,加载过的不会释放
大图滑动 V1 FPS 10-15 不断创建新的 view,不断释放 在 scrollViewDidEndDecelerating 才加载,太 慢
进列表时间很长,初始化东西太多
大图滑动 V1.1 动态补充 loadingView 将绘制逻辑改到 scrollViewWillBeginDragging 缓存第二层的 zoomingScrollView
大图滑动 V1.1 进列表时间有所改善 所有第⼀一次翻动都很卡 FPS: 10-15 Cache 过的 view 不需重新创建
FPS: 20-30
大图滑动 V1.2 提前加载左右 N 页 判定是否到了 N 页,再进行加载 N =
5
大图滑动 V1.2 预加载左右 5 张,进列表时间又长了⼀一点 左右 5 张之内 FPS 30-40
第 6 张⼀一次性又要加载5张 FPS 10-20
大图滑动 V1.x zoomingScrollView 基本不可重用 大图缓存的命中率很低 每⼀一次绘制都需要遍历三个N长数组 图片,loadingView,zoomingScrollView 每次翻动的时间跟列表长度相关 O(N) 创建后不移出
view,subviews 越来越多
大图滑动 V2 不采用单独的 loadingView 数组和 [NSNull null] 用 zoomingScrollView 来引用
loadingView 跟 image View的缓存和回收 NSMutableSet 无序,NSObject 不重复 visiblePages / recyclePages
大图滑动 V2 scrollViewDidScroll 判定当前可见的页(<=2) 如果需要绘制,从 recyclePages 里 dequeue ⼀一个放到 visiblePages
回收不可见的页,从 visiblePages 里挪到 recyclePages 如果不需要绘制,什么都不做
大图滑动 V2 FPS 40-50 保持外层 UIScrollView 的 subviews,最多只 有两个 减少不断创建新
view 的开销 减少 scrollViewDidScroll 时的遍历开销 O(1)
进化路线 滑动时的 FPS 15 => 45 滑动时的遍历:O(n) => O(1) 内存增长:
view 的创建和回收: N => 2
Next? 大图内存没有尽快释放 [obj release] 只是引用-1,并没有马上释放 采用小图策略,DB 保存等比小图 ⼀一次加载到内存,内存只缓存小图 (20k *
100) 快速滑动时拉伸小图,模糊,但是快 scrollViewDidEndDecelerating 时绘制大图 view 不可见时马上清大图 image = nil;
And.. [NSURLConnection sendSynchronousRequest:returningResponse:error:] 所有网络数据都 load 到内存,并发下载三个, 内存涨了 10M (Oh my!)
对可能的大图片,异步 IO + 文件流
Thanks Q&A