1、相册相关
PHAssetCollection 相册集合
PHAsset 每一个相册资源,从PHAssetCollection中获取
modificationDate 修改时间,
PHFetchOptions一些操作
根据修改时间排序
PHFetchOptions *option = [[PHFetchOptions alloc] init];
option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:YES]];
2、weak的释放原理
对象释放 -> objc_release -> dealloc -> objc_rootdealloc ->
objc_dispose -> objc_destructinstance -> objc_clear_deaallocation ->
通过两次hash索引找到weak表 把所有的引用全部置空。
3、autoreleasePool 的结构和自动释放池中的对象存储过程,autoreleasePool的结构
这个结构体在初始化是调用objc_autoreleasePoolPush(),在析构时调用objc_autoreleasePoolPop()。
每一个自动释放池其实是一个双向链表,链表的每一个结点就是这个AutoreleasePoolPage,每个AutoreleasePoolPage的大小为4096字节
调用objc_autoreleasePoolPop时,会使用一个POOL_SENTINEL(哨兵),根据传入的哨兵对象地址找到哨兵对象所处的page。在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次- release消息,并向回移动next指针到正确位置。
因为自动释放池是一个双向链表,而且每一个page的空间有限,所以会存在当前page已满的情况,也就出现了一个自动释放池跨越几个page的情况,所以在release的时候,也要顺着链表全部清理掉。
资源释放是在runloop迭代结束时释放的,而并非作用域结束。
4、NSSet、NSDictionary、NSArray
set、dic用的hash。array用的数组。对应到可变类型。arrayM用了环形缓冲区。
5、查找方法调用流程
(1,按照方法名去cache_t中查找方法,找到了就直接调用。
(2,1中未找到,进入类对象方法列表中查找。这个列表包含了分类的方法。
(3,如果在方法列表中找到了方法,则首先将方法加入cache_t中,然后调用方法。
(4,如果在方法列表中未找到方法,会通过superclass找到父类,在父类中进行1、2、3步骤。
(5,如果最终未找到方法,会进入方法的动态解析阶段。
6、解决方法未找到的crash?
重写签名方法,返回一个非nil的方法签名,以及forwardInvocation方法。
7、cache__t的k和v?
Key---@select value ——imp
8、block捕获变量
int val = 1; 值传递(根本原因:Block不允许修改栈中指针的内容)
static int static_val = 1; 指针传递(非对象,存储在静态区,局部静态变量,因为是局部,所以外部无法访问)
__block后被封装成为结构体
__block说明符将基本类型的数据封装为结构体类型(其中包含了isa指针),这其实就说明__block变量已经是作为了一个对象在使用。这里也并不违背括号中说的block无法修改栈中数据。归根结底还是因为栈是系统管理的,外部人员无法操控。
9、load()和initialize()区别
load 分类不会替换掉主类的方法。initialize会。
load启动即可调用。initialize类使用时候调用,比alloc早。
load是根据函数地址直接调用,initialize是通过objc_msgSend调用。
先调用类的load, 在调用分类的load。先编译的类, 优先调用load, 调用子类的load之前, 会先调用父类的load。先编译的分类, 优先调用load
initialize 先初始化分类, 后初始化子类,通过消息机制调用, 当子类没有initialize方法时, 会调用父类的initialize方法, 所以父类的initialize方法会调用多次,调用多次的时候,获取的类名会不一样。扩展会把initialize拦腰折断,只有扩展对应的父类会调用,自己和自己的子类不会调用,只会调用对应扩展。
10、图层方法两倍形变后,frame和bouns的变化,相对位置,绝对位置。
frame相对中心,框一个矩形。bounds不变。
11、nstimer不准怎么办?
加入特性mode。新建子线程去创建timer。使用gcd的dispath_source_t
12、strong和copy的区别
区别在于可变对象的浅拷贝和深拷贝。
13、IOS多线程三种方式的优缺点
NSThread 轻量级。但是线程同步需要自己管理。
operation 无需关心线程。对gcd的封装。
GCD 不是oc风格,是c的。
14、iOS开发中的常用锁以及性能对比
NSLock、dispatch_semaphore_t、synchronized。
synchronized性能最差,但是最方便。
15、异步改同步
《1、semaphore 《2、group 《3、bool值 《4、nslock
16、block会对外部局部变量进行浅拷贝,然后在block体内使用该副本。
17、同步异步,串行并行队列。
异步串行(n个队列)会生成对应个数线程(n)。异步并行(个n队列)会生成多个线程(>=n)
18、DISPATCH_QUEUE_SERIAL_INACTIVE是啥?
未初始化的串行队列,一般不用。
19、dispatch_barrier_sync和dispatch_barrier_async?
共同点:
《1、等待在它前面插入队列的任务先执行完
《2、等待他们自己的任务执行完再执行后面的任务
不同点:
《1、dispatch_barrier_sync将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们
《2、dispatch_barrier_async将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务。
barrier 在global_queue中失效。
20、LLVM编译流程
LLVM是一个模块化的、可重用的编译器和工具链技术的集合,Clang 是 LLVM 的子项目。
clang -ccc-print-phases main.m
进行语法分析、语义分析以及生成中间代码.LLVM核心库还提供一个优化器。
开启bitcode优化:
clang -emit-llvm -c main.m -o main.bc
生成汇编的命令:
clang -S -fobjc-arc main.m -o main.s
再生成目标文件:
clang -fmodules -c main.m -o main.o
最后生成可执行文件:
clang main.o -o main
过程:预处理(语法、词法),编译(编译优化),汇编,链接,绑定相应变量或者常量。
21、优化 App 的启动时间实践 iOS
其中 t1苹果提供了内建的测量方法, Xcode 中 Edit scheme -> Run -> Auguments 将环境变量 DYLD_PRINT_STATISTICS 设为 1….
算了,这个需要研究好久,不纠结了,毕竟不是干这个的。
22、事件传递与响应?
当我们触摸屏幕的时候,为我们找到最合适的view。
每当手机触摸屏幕,操作系统会把事件传递给当前的APP,在UIApplication接收到手指的事件之后,就会去调用 UIWindow 的 hitTest:withEvent: 方法,看看当前点击的点是不是在window内,如果是则继续调用其subview的 hitTest:withEvent: 方法,直到找到最后需要的view。
hitTest:withEvent: 的内部实现
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.hidden || self.userInteractionEnabled == NO || self.alpha < 0.01 || [self pointInside:point withEvent:event]) {
return nil;
}
for (UIView *subView in self.subviews.reverseObjectEnumerator) {
UIView *hitView = [subView hitTest:[subView convertPoint:point fromView:self] withEvent:event];
if (hitView) {
return hitView;
}
}
return self;
}
传递与响应正相反。
23、scrollview嵌套。
contentOffset用于限制滚动。
24、子线程是否要手动创建autoreleasepool
NSThread和NSOperationQueue开辟子线程需要手动创建autoreleasepool,GCD开辟子线程不需要手动创建autoreleasepool,因为GCD的每个队列都会自行创建autoreleasepool
25、为什么不能在子线程刷新UI
为了安全,因为UIKit框架不是线程安全的,当多个线程同时操作UI的时候,抢夺资源,导致崩溃,UI异常等问题。
即使子线程操作UI了,也仅仅是给人的印象是在子线程操作的,而实际上必须等到线程结束后到主线程更新UI,并且代码处会出现紫色。
26、autoreleasePool 在何时被释放?
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是 -2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
27、Runloop何时退出?
线程结束或者设置了过期时间。
28、互斥锁、自旋锁?
互斥锁睡眠,自旋锁死循环。
29、Block中捕获全局、静态、实例、局部变量是怎样实现的?
自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值。Block捕获的外部变量可以改变值的是静态变量,静态全局变量,全局变量。
30、什么是内存对齐?
对齐规则是按照成员的声明顺序,依次安排内存,其偏移量为成员大小的整数倍,0看做任何成员的整数倍,最后结构体的大小为最大成员的整数倍。
31、卡顿产生的原因?
离屏渲染、主线程大量运算。让主线程干重活,例如网络请求,读写大文件,复杂的运算 等一些耗费大量系统资源及时间的任务
32、crash原因?
调用野指针,数组越界,除零(arm貌似不会,x86系列会,因此iPhone好像不会有问题),未实现方法,函数不支持当前系统,空cell,列表中有nil,内存越界。
33、iOS App的启动过程?
加载dyld到App进程,加载动态库(包括所依赖的所有动态库),Rebase,Bind,初始化Objective C Runtime,其它的初始化代码。
34、iOS图像显示原理和卡顿优化?
先是cpu,进行UI布局,文本计算;执行drawrect;图像编码,提交到GPU总线。Gpu依次进行顶点着色,图元装配,光栅化,片段着色,片段处理