iOS 卡顿总结优化
2016-12-08 17:08
701 查看
1.除了UI部分,所有的加载操作都在后台完成。
1.1 文本计算
如果一个界面中包含大量文本,文本的宽高计算会占用很大一部分资源,并且不可避免。如果你对文本显示没有特殊要求,可以参考下 UILabel内部的实现方式:用 [NSAttributedStringboundingRectWithSize:options:context:]
来计算文本宽高,
用 -[NSAttributedStringdrawWithRect:options:context:]
来绘制文本。尽管这两个方法性能不错,但仍旧需要放到后台线程进行以避免阻塞主线程。
如果你用 CoreText
绘制文本,那就可以先生成 CoreText
排版对象,然后自己计算了,并且 CoreText
对象还能保留以供稍后绘制使用。
1.2文本渲染
屏幕上能看到的所有文本内容控件,包括 UIWebView,在底层都是通过 CoreText排版、绘制为 Bitmap
显示的。常见的文本控件 (UILabel、UITextView
等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU
的压力会非常大。对此解决方案只有一个,那就是自定义文本控件,用 TextKit
或最底层的 CoreText
对文本异步绘制。
尽管这实现起来非常麻烦,但其带来的优势也非常大,CoreText
对象创建好后,能直接获取文本的宽高等信息,避免了多次计算(调整 UILabel
大小时算一遍、UILabel
绘制时内部再算一遍);CoreText
对象占用内存较少,可以缓存下来以备稍后多次渲染。
1.3图片的解码
用 UIImage或CGImageSource
的那几个方法创建图片时,图片数据并不会立刻解码。图片设置到 UIImageView
或者 CALayer.contents
中去,并且 CALayer
被提交到 GPU 前,CGImage
中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。如果想要绕开这个机制,常见的做法是在后台线程先把图片绘制到 CGBitmapContext
中,然后从 Bitmap
直接创建图片。目前常见的网络图片库都自带这个功能。
1.4图像的绘制
图像的绘制通常是指用那些以 CG开头的方法把图像绘制到画布中,然后从画布创建图片并显示这样一个过程。这个最常见的地方就是 [UIView drawRect:]
里面了。由于CoreGraphic
方法通常都是线程安全的,所以图像的绘制可以很容易的放到后台线程进行。一个简单异步绘制的过程大致如下(实际情况会比这个复杂得多,但原理基本一致):
- (void)display {
dispatch_async(backgroundQueue,^{
CGContextRef ctx =CGBitmapContextCreate(...);
// draw in context...
CGImageRef img =CGBitmapContextCreateImage(ctx);
CFRelease(ctx);
dispatch_async(mainQueue, ^{
layer.contents = img;
});
});
}
可以通过dispatch或者performSelectorInBackground或者NSOperationQueue来实现。
1.5 I/o操作
文件读写网络
2. 避免后台加载完成多个资源之后集中到达占用UI线程的处理时间太长。(短时间内UI集中刷新)
通过NSOperationQueue来实现,将资源到UI的展现过程放在队列中逐个执行,且在每个操作完成之后进行强制等待,可以用usleep(int
microSeconds)来解决。
3.线程数太多
原因:(1)大量的任务提交到后台队列时,某些任务会因为某些原因被锁住导致线程休眠,或者被阻塞。
concurrentqueue 随后会创建新的线程来执行其他任务。
当这种情况变多时,或者 App
中使用了大量 concurrent queue
来执行较多任务时,App
在同一时刻就会存在几十个线程同时运行、创建、销毁。
CPU 是用时间片轮转来实现线程并发的,尽管 concurrent queue
能控制线程的优先级,但当大量线程同时创建运行销毁时,这些操作仍然会挤占掉主线程的 CPU
资源。
(2)线程数量达到 64个,导致也是导致卡顿的原因。
4 使用lock 时 ,不能Unlock 造成
解决方式: 使用自旋锁,递归锁。-(void) dispatch_reentrant(void(^block)())
{
staticNSRecursiveLock *lock = nil;
staticdispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lock =[[NSRecursiveLock alloc]init];
});
[locklock];
block();
[lockunlock];
}
dispatch_queue_t queueA = dispatch_queue_create("com.queueA", NULL);
dispatch_block_t block = ^{
//do something
};
dispatch_sync(queueA, ^{
dispatch_reentrant(block);
});
5 使用 GCDdispatch_sync 死锁卡顿
queueA—>queueB—>queueA
- (void)deadLockFunc
{
dispatch_queue_t queueA =dispatch_queue_create("com.queueA", NULL);
dispatch_queue_t queueB =dispatch_queue_create("com.queueB", NULL);
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
dispatch_block_t
block = ^{
//do something
};
func(queueA,
block);
});
});
}
解决方式, FMDB,AFNetWork
中 采用dispatch_queue_set_specific
,dispatch_get_specific标记 队列 ,然后 进行判断处理,或给出log告警
dispatch_queue_t queueA =dispatch_queue_create("com.queueA", NULL);
dispatch_queue_t queueB =dispatch_queue_create("com.queueB", NULL);
dispatch_set_target_queue(queueB, queueA);
static int specificKey;
CFStringRef specificValue =CFSTR("queueA");
dispatch_queue_set_specific(queueA,
&specificKey,
(void*)specificValue,
(dispatch_function_t)CFRelease);
dispatch_sync(queueB, ^{
dispatch_block_t block = ^{
//do something
};
CFStringRef retrievedValue =dispatch_get_specific(&specificKey);
if (retrievedValue) {
block();
} else {
dispatch_sync(queueA,
block);
}
});
6 视图的混合
当多个视图(或者说 CALayer)重叠在一起显示时,GPU会首先把他们混合到一起。如果视图结构过于复杂,混合的过程也会消耗很多 GPU
资源。为了减轻这种情况的 GPU
消耗,应用应当尽量减少视图数量和层次,并在不透明的视图里标明 opaque
属性以避免无用的 Alpha
通道合成。当然,这也可以用上面的方法,把多个视图预先渲染为一张图片来显示。
7 主线程中UI对象创建
对象的创建会分配内存、调整属性、甚至还有读取文件等操作,比较消耗 CPU
资源。尽量用轻量的对象代替重量的对象,可以对性能有所优化。
比如 CALayer
比 UIView
要轻量许多,那么不需要响应触摸事件的控件,用 CALayer
显示会更加合适。如果对象不涉及 UI
操作,则尽量放到后台线程去创建,但可惜的是包含有 CALayer
的控件,都只能在主线程创建和操作。
·
参考: http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
相关文章推荐
- ios 地图 自定义 callout (气泡) (可同时显示多个气泡)
- was mutated while being enumerated.
- iOS - 关于轮播图的实现实例
- iOS 实现模糊搜索的功能
- 浅谈IQKeyboardManager第三方库的使用
- iOS与js 的简单交互
- iOS 约束冲突怎么解决
- iOS——MBProgressHUD的使用方法
- iOS开发之-- 设置启动图片
- AudioKit 教程:入门
- 【转】IOS图像拉伸解决方案
- iOS 约束冲突解决方法
- iOS开发之即时通讯之Socket(AsyncSocket)
- iOS中将毫秒转换成时长
- iOS 驾驭 StoryBoard
- iOS 计算两个日期字符串的差值
- ios9 新关键字 __kindof 等(etc) 小结
- iOS 转场动画
- 史上最详细的iOS之事件的传递和响应机制
- IOS相关笔记