1、代理和Block
block其实是一个对象,代理是一种设计模式(委托-代理模式),
(1)全局静态 block,不会访问任何外部变量,执行完就销毁。
^{
NSLog(@"Hello World!");
}();
(2)保存在栈中的 block,当函数返回时会被销毁,和第一种的区别就是调用了外部变量。[UIView animateWithDuration:3 animations:^{ self.view.backgroundColor = [UIColor redColor]; }];
(3)保存在堆中的 block,当引用计数为 0 时会被销毁。
2、动画
1. 简单动画 :
animateWithDuration:animations
带调节参数的 + animateWithDuration:delay:options:animations:completion:
animateKeyframesWithDuration:
3, View 转换
+ transitionWithView:duration:options:animations:completion:
+ transitionFromView:toView:duration:options:completion:
4. CALayer Animation : CALayer 动画之前,首先需要引入 QuartzCore.framework
CABasicAnimation 用于创建一个 CALayer 上的基本动画效果,CAKeyframeAnimation 关键帧动画
CAAnimationGroup) 组动画(
CATransition 切换动画
5. 更高级的动画效果 CADisplayLink + Draw
6. UIDynamicAnimator 具有物理仿真效果的动画
7. CAEmitterLayer 是 Core Animation 提供的一个粒子发生器系统,可以用于创建各种粒子动画
3、设计模式
1. 单例模式
Cocoa 库本身在一些地方也使用了单例模式,这种写法的优点是,可以延迟加载,按需分配内存以节省开销。
这并非一个线程安全的写法,比如两个或多个线程并发的调用 sharedInstance 方法,有可能会得到多个实例,
static
if (!instance) {
instance = [[super allocWithZone:NULL] init];
} 这里列出两种方法来创建一个线程安全的单例
1 . 可以使用@synchronized进行加锁
2. GCD中的dispatch_once方法, 是最普遍也是苹果最推荐的方法
2. 工厂模式
工厂模式 本质上是 使用方法 来 简化类的选择 和 初始化过程
@implementation OperationFactory
+ (Operation *) createOperat:(char)operate{
Operation *oper = nil;
switch (operate) {
case '+':
{
oper = [[OperationAdd alloc] init];
break;
}
case '-':
{
oper = [[OperationSub alloc] init];
break;
}
return oper;
}由于 Objective-C 本身的动态特性,还可以用反射来改写:
@implementation OperationFactory
+ (Operation *) createOperat:(NSString *)operate{
Operation *oper = nil;
Class class = NSClassFromString(operate);
oper = [(Operation *)[class alloc] init];
if ([oper respondsToSelector:@selector(getResult)]) {
[oper getResult];
}
return oper;
}
@end
使用时,可以传入类名,来获取对应类的对象:
Operation *oper = [OperationFactory createOperat: @"OperationAdd"];
oper.numberA = 10;
oper.numberB = 20;
NSLog(@"%f", oper.getResult);
3. 委托模式 (Delegate)
委托模式通常使用协议(protocol)来实现
4. 观察者模式
两种用于实现观察者模式的办法,一直是使用NSNotification,另一种是KVO(Key Value Observing
6、性能
1. 离屏渲染
离屏渲染往往会带来界面卡顿的问题,这里将会讨论 当前屏幕渲染、离屏渲染 以及 CPU 渲染
当前屏幕渲染,在用于显示的屏幕缓冲区中进行,不需要额外创建新的缓存,也不需要开启新的上下文,所以性能较好,但是受到缓存大小限制等因素,一些复杂的操作无法完成
离屏渲染,指的是在 GPU 的当前屏幕缓冲区外开辟新的缓冲区进行操作。
离屏渲染的代价是很高的,体现在 :
1. 创建新的缓冲区
2. 上下文切换。离屏渲染的整个过程,需要多次切换上下文环境:先从当前屏幕切换到离屏,等待离屏渲染结束后,将离屏缓冲区的渲染结果显示到到屏幕上,这又需要将上下文环境从离屏切换到当前屏幕。
以下属性时,会触发离屏渲染:
- shouldRasterize(光栅化)
- masks(遮罩)
- shadows(阴影)
- edge antialiasing(抗锯齿)
- group opacity(不透明)
避免使用 layer 的 border、corner、shadow、mask 等技术
CPU 渲染和离屏渲染的区别?
1. GPU的浮点运算能力比CPU强,CPU渲染的效率可能不如离屏渲染。
但 如果仅仅是实现一个简单的效果,直接使用 CPU 渲染的效率又可能比离屏渲染好,毕竟普通的离屏渲染要涉及到缓冲区创建和上下文切换等耗时操作。
2. 对一些简单的绘制过程来说,这个过程有可能用CoreGraphics,全部用CPU来完成反而会比GPU做得更好。一个常见的 CPU 渲染的例子是:重写 drawRect 方法,并且使用任何 Core Graphics 的技术进行了绘制操作,就涉及到了 CPU 渲染。整个渲染过程由 CPU 在 App 内同步地完成,渲染得到的bitmap最后再交由GPU用于显示。
总之,具体使用 CPU 渲染还是使用 GPU 离屏渲染更多的时候需要进行性能上的具体比较才可以。
一个常见的性能优化的例子 : 加圆角
三种加圆角的方式:
- 设置 cornerRadius
cornerRadius+ masksToBounds 会触发两次离屏渲染,如果加之滚动 就会性能问题
通过缓存优化 shouldRasterize 光栅化YES + rasterizationScale ,光栅化 会把使视图渲染内容被缓存起来,下次绘制的时候可以直接显示缓存,
注意:png 图片 在 UIImageView 这样处理圆角是不会产生离屏渲染的( ios9 后不会,前会) - UIBezierPath
drawRect:+ UIBezierPath 这种方法会触发一次离屏渲染 但是这种方式会导致内存暴增,并且同样会触发离屏渲染。 - Core Graphics (为 UIView 加圆角)与直接截取图片(和 为 UIImageView 加圆角)
这种做法的原理是利用 Core Graphics 自己画出了一个圆角矩形。
UIGraphicsBeginImageContextWithOptions
UIGraphicsGetCurrentContext
CGContextMoveToPoint
CGContextDrawPath
UIGraphicsEndImageContext
这个方法返回的是 UIImage,有了这个图片后,就可以创建一个 UIImageView 并插入到视图层级的底部:总结
- 如果能够只用 cornerRadius 解决问题,就不用优化。
- 如果必须设置 masksToBounds,可以参考圆角视图的数量,如果数量较少(一页只有几个)也可以考虑不用优化。
- UIImageView 的圆角通过直接截取图片实现,其它视图的圆角可以通过 Core Graphics 画出圆角矩形实现。
7. 硬解码
硬编码:不使用CPU进行编码,使用显卡GPU,专用的DSP、FPGA、ASIC芯片等硬件进行编码。
1. 将H264码流转换为解码前CMSampleBuffer对象
NALU单元组成,纳鲁单元包含视频图像数据CMBlockBuffer和H264的参数信息则可以组合成FormatDesc,CMSampleBuffer对象,包括CMTime、CMVideoFormatDesc、CMBlockBuffer等,我们解码的任务就是从H264码流里面提取上面三处的信息,合成解码后的CMSampleBuffer对象,提供给硬解码接口进行解码工作。2. 硬解码后的图像显示
下面我们就看一下硬解码后的图像显示,具体的显示方式有两种:
- 通过系统提供的
AVSampleBufferDisplayLayer来解码并显示。 - 通过
VTDecompression接口来,将CMSampleBuffer解码成图像,将图像通过UIImageView或者OpenGL上显示。
通过系统提供的AVSampleBufferDisplayLayer来解码并显示
AVSampleBufferDisplayLayer是苹果提供的一个专门显示解码后的H264数据的显示层,它是CALayer的子类,因此使用方式和其它CALayer类似。使用方法enqueueSampleBuffer :进行显示该层内置了硬件解码功能,将原始的CMSampleBuffer解码后的图像直接显示在屏幕上面,如下图所示。
通过 VTDecompression 接口来,将CMSampleBuffer解码成图像,将图像通过UIImageView或者OpenGL上显示
将解码后的CMSampleBuffer数据解码成 CVPixelBufferRef 数据,
将 CVPixelBufferRef 装换成 UIImage并显示
解码方式一
优点: 该方式通过系统提供的
AVSampleBufferDisplayLayer显示层来解码并显示。该层内置了硬件解码功能,将原始的CMSampleBuffer解码后的图像直接显示在屏幕上,非常的简单方便,且执行效率高,占用内存相对较少。缺点: 从解码的数据中不能直接获取图像数据并对其做相应处理,解码后的数据不能直接进行其他方面的应用(一般要做较复杂的转换)。
解码方式二
优点: 该方式通过
VTDecompressionSessionDecodeFrame接口,得到CVPixelBufferRef数据,我们可以直接从CVPixelBufferRef数据中获取图像数据并对其做相应处理,方便于其他应用。缺点: 解码中执行效率相对降低,占用的内存也会相对较大。
8. 硬编码
硬编码我们也经常见,比如说,我们直播录制视频,就要先通过摄像头采集图像,然后进行硬编码,最后将硬编码后的数据组合成H264码流通过网络传播。
1. 视频采集
硬件设备就是摄像头了,通过AVFoundation框架中的AVCaptureSession类来采集图像,并设定好input和output,同时设定deleagte代理和输出队列,在代理delegate方法中,处理采集好的图像。图像输出的格式是未编码的CMSampleBuffer形式。
2. 使用VTCompressionSession进行硬编码
来对该帧进行硬编码,编码成功的CMSampleBuffer转换成H264码流,通过网络传播9. 软编码
视频采集后 H264进行编码,iOS 11 之后,iPhone 7以上的设备可以支持新的编码器H265编码器,使得同等质量视频占用的存储空间更小。所以本例中可以使用两种方式实现视频数据的编码
- 软编码:使用CPU进行编码。
这里借助FFmpeg //打开视频文件,将文件格式的上下文传递到AVFormatContext类型的结构体中 //查找文件中视频流的信息 //找到该视频流对应的解码器 //打开解码器 //pFrame = av_frame_alloc后面就是将YUV数据转换为RGB,然后将RGB图像转换为UIImage, 便可以在屏幕上显示出来了可以参考的是 KXMovie
软硬区别对比
苹果在iOS 8.0系统之前,没有开放系统的硬件编码解码功能,不过Mac OS系统一直有,被称为Video ToolBox的框架来处理硬件的编码和解码,终于在iOS 8.0后,苹果将该框架引入iOS系统
H265优点
- 压缩比高,在相同图片质量情况下,比JPEG高两倍
- 能增加如图片的深度信息,透明通道等辅助图片。
- 支持存放多张图片,类似相册和集合。(实现多重曝光的效果)
- 支持多张图片实现GIF和livePhoto的动画效果。
- 无类似JPEG的最大像素限制
- 支持透明像素
- 分块加载机制
- 支持缩略图