Fork me on GitHub

iOS面试题笔记

写一个标准宏 MIN/MAX

利用 __typeof__ 重新声明 2 个相同类型的变量,在把相关的值赋值给新的变量,在进行比较,这样可以排除其他的各种优先级问题。

1
2
#define kMIN(a,b) ({__typeof__(a) __a__COUNTER__ = (a);__typeof__(b) __b__COUNTER__ = (b);(__a__COUNTER__) < (__b__COUNTER__) ? (__a__COUNTER__) : (__b__COUNTER__);})
#define kMAX(a,b) ({__typeof__(a) __a__COUNTER__ = (a);__typeof__(b) __b__COUNTER__ = (b);(__a__COUNTER__) > (__b__COUNTER__) ? (__a__COUNTER__) : (__b__COUNTER__);})

圆角卡顿问题

卡顿的原因?

屏幕显示图像的原理是从上到下一行行继续扫描【当扫描完一行时发出一个HSyncx信号,扫描完整个屏幕时发出一个VSyncx信号】

当收到VSyncx信号时 CPU需把相关需要显示的内容交给GPU来渲染,渲染好了交到帧缓冲区准备显示,在下一帧显示的时候直接到帧缓冲区取出显示即可以,

理想状态的这样的,但是如果CPU或者GPU任务繁重,在需要显示的时候还没有准备好,就无法显示,会导致本来应该显示新的内容还会停留在上一帧,

可能造成跳帧的情况,卡顿就产生了,使用卡顿的本质就是 CPU 和 GPU任务未完成导致的。

圆角为什么可能导致卡顿呢?

正如【 卡顿的原因?】所诉,为了尽量不卡顿,所以推出多个帧缓冲区的概念,正所谓鱼和熊掌不可兼得,多个帧缓冲区虽然可以解决一些卡顿问题,

但帧缓冲区之间切换又是一个特别耗性能的问题,在设置圆角时【主要是 masksToBounds masksToBounds】就是在其他的帧缓冲区处理的【称:离屏渲染】

如果一个屏幕上太多圆角时就会导致频繁的帧缓冲区切换,消耗太多的 GPU性能,导致在收到VSyncx信号到显示 GPU没有处理完成,导致卡顿的产生。

那么怎么解决呢?

正如【圆角为什么可能导致卡顿呢?】中所诉,GPU太忙了,但此时CPU可能比较闲,那么我们是否可以想办法把一些任务分配给CPU来处理呢,

通常的处理方案就是如此:

预先用CPU,构建圆角路径贝塞尔曲线UIBezierPath,用原来的图片填充进圆角路径,获得天然的自带圆角透明的bitmap数据UIImage,从而直接交给GPU进行普通渲染【不使用 masksToBounds masksToBounds】。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@implementation UIImage (BMKit)
- (instancetype)_imageAddCornerWithRadius:(CGFloat)radius size:(CGSize)size {
//0、获取 size
CGRect rect = CGRectMake(0, 0, size.width, size.height);
//1、开启图形上下文
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
//2、获取当前的图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//3、创建路径 path
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
//4、把路径添加上下文
CGContextAddPath(ctx,path.CGPath);
//5、剪切
CGContextClip(ctx);
//6、绘制图片
[self drawInRect:rect];
//7、设置填充的样式
CGContextDrawPath(ctx, kCGPathFillStroke);
//8、取得图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//9、关闭图形上下文
UIGraphicsEndImageContext();
return newImage;
}

是不是全部用CPU来处理就彻底解决了圆角问题呢?

当然不是的,如果我们全部都给CPU来处理,CPU的任务重了同样可能造成卡顿问题,通常情况下:

1、小量圆角直接使用 masksToBounds masksToBounds 即可,因为如果使用CPU来处理可能反而适得其反。

2、较多圆角时使用CPU来处理可以很好的处理卡顿问题,同时可以集合一些缓存策略。

3、如果实在无法解决时可以使用异步渲染的方式来处理参考:AsyncDisplayKit

demo - https://github.com/liangdahong/iOS-Interview-Notes/tree/master/002-CornerRadius-Demo0

003-响应链

触摸事件的传递和响应过程

在触摸事件的处理过程中会有 事件传递 和 事件响应过程,首先当用户触摸屏幕时,会从上向下

(UIApplication->keyWindow->vc->View->View1... 会判断范围是否包含,是否开启了响应,是否设置了透明度等)

找到最合适的对象,当找到最合适的View时会由下向上

(...View1->keyWindow-> View-> vc-> keyWindow-> UIApplication 会判断是否加了相关处理的方法)

找最合适响应的对象。

参考

  1. http://blog.flight.dev.qunar.com/2016/10/28/ios-event-mechanism-summary/
  2. http://www.jianshu.com/p/2e074db792ba

Runloop

Runloop

就是为了应用在有事做的时候干活,没事做时休眠,具体是使用 内核函数 mach_msg Mach_port来实现线程的休眠,和激活。

runloop默认是没有创建的,只可以获取,懒加载的形式,runloop 和 线程是一个哈希关系,线程是 k,runloop 是 v,子线程中只可以获取当前线程的runloop或者max runloop。

Runloop里分 mode 模式,每一个模式里面有 soure0 soure1 timer obje 来处理。
循环处理相应模式下的任务,在切换模式的时候被切换的模式下的任务就会暂停,比如:定时器默认情况下载 scrollView 跟踪的时候就暂停了。

1、定时器在 scrollView 跟踪也处理可以使用 把定时器加到相应的模式下,或者直接标记为 COMM ,COMM不是一个具体的模式,只是把相应的 item 加到 COMM 中。又因为 【默认模式】和 【跟踪模式】都是 COMM 所以一举两得。
2、线程保活,需要处理 runloop,同时需要指定模式run起来,还需要 while 和 flag 来处理是否关闭处理,【注意使用 run 开启 runloop 是无法关闭的】
3、唤醒,是系统内核唤醒的;休眠,是系统内核让线程休眠的R

内存管理

arc : 其实是编译器特性,编译器在编译的时候主动加上了内存管理的代码。
mrc: 在 arc 之前是程序员手动管理内存。
oc 的内存回收机制使用了引用计数来处理的,当引用计数=0 的时候就释放掉对象,引用计数是在 对象的 isa 中存起来的,具体释放是在 release 的时候判断 引用计数是不是为0了,如果是就释放。

内存布局情况:

保留【系统使用】

代码段

数据段【。。。】

s

s

s

s

s

s

autorelease 和 自动释放池。
自动释放池,其实底层用到了【 c++结构体 4096 ,stack,双向链表】来实现的,在使用 >autoreleasepool 的时候底层会在 autoreleasepool 开始的时候 做push 操作,【push操作其实就>是在加入一个标记,然后每调 autolease 方法就会把此对象的地址加到 C++结构体的指定位置上,在 autoreleasepool 结束的时候 调用 pop 方法,底层其实就是 从这个位置向后处理刚才加到 c++结构体指定位置的对象执行 release 操作,当到 标记的地方就结束,这里就用到了stack,同时如果如果autolease的对象比较多的时候,这里会把 c++结构体 穿起来做成 双向链表,【在push的时候如果已经满了就去链表的下一个对象存储,在 pop 操作的时候如果一直没有找到标记就向上一个中去继续查,直到找到标记为止】】

同时 runloop 循环中也牵涉到 autoreleasepool
1、在进入 runloop 的时候 push 操作,
…工作…
2、在休眠之前执行执行 pop 然后执行 push
=====休息时间=====
3、唤醒
…工作..
->2 或者 5
5、退出 pop
所以如果所以 autoreleasepool 方法中的 使用autorelease的临时变量不一定在离开方法就释放。

001

  • dyld 装载app的可执行文件,同时加载依赖的动态库,然后通知 runtime 来处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 中间代码
// cd 到指定的m文件 clang -emit-llvm -S xxx.m


// C++ 代码
// cd 到指定的m文件
// xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxx.m


// 看汇编代码
// 断点看汇编代码: -> Debug -> Work flow -> Always Show Disassembly
// 直接看汇编代码: 选中m文件 -> Prudect -> Perform Action -> Assembly


(lldb) p/x per->isa
(Class) $13 = 0x001d800100003631 BMPerson
(lldb) p/x clas
(Class) $14 = 0x0000000100003630 BMPerson

(( per->isa)& 0x00007ffffffffff8ULL) = 0x0000000100003630
(( 0x001d800100003631)& 0x00007ffffffffff8ULL) = 0x0000000100003630

# define ISA_MASK 0x00007ffffffffff8ULL

KVO

- END -
扫一扫上面的二维码图案,加我微信

文章作者:梁大红

特别声明:若无特殊声明均为原创,转载请注明,侵权请联系

版权声明:署名-非商业性使用-禁止演绎 4.0 国际