准备:
1.常用的设计模式
2.用过哪些开源框架
3.多线程
4.instruments
5.runtime
6.TCP/IP
7.HTTP
8.内存管理
9.GUI
10.git
今天遇到的面试题
1.block内部的延时操作如何处理?
使用__weak,也会有一个隐患,在block内部如果调用了延时函数还使用弱指针会娶不到该指针,因为已经被销毁了,为了保证在block内不会被释放,我们添加__strong。更多的时候需要配合strongSelf使用:
__weak typeof(self) weakSelf = self;
self.testBlock = ^{
__strong __typeof(weakSelf) strongSelf = weakSelf;
[strongSelf test];
});
总结:这个知识点我是知道的,项目里也是这样使用的,但是直接问我延时函数我没有想到是要问我关于关键字的知识点,还以为是要考我关于多线程的问题。以后遇到Block里面嵌套GCD应该马上想到关键字要添加_strong。
2.消息推送机制
推送通知的过程可以分为以下几步:
1.应用服务提供商从服务器端把要发送的消息和设备令牌(device token)发送给苹果的消息推送服务器APNs。
2.APNs根据设备令牌在已注册的设备(iPhone、iPad、iTouch、mac等)查找对应的设备,将消息发送给相应的设备。
3.客户端设备接将接收到的消息传递给相应的应用程序,应用程序根据用户设置弹出通知消息。
总结:我只说出了,服务器端发消息给苹果的APNS,APNS再发消息给客户端,说的不够具体,应该提到通过device token 查找相应的设备。
3.图片圆角的处理方法
共有4种方法
1.通过设置layer的属性
最简单的一种,但是很影响性能,一般在正常的开发中使用很少.
2.使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
3.使用CAShapeLayer和UIBezierPath设置圆角
4.使用带圆形的透明图片.(需要UI做一个切图)
总结:这个问题四年前面试的时候面试官就问过我,当时也是没答上来,虽然代码可能背不下来,但是至少应该知道是用贝塞尔曲线画出来的。以前项目中有用户列表的时候会显示很多头像,圆形的图片太多会影响性能,当时的做法是给UIImageView添加了一个分类,因为是在网上直接搜的方法也没有自己研究里面的代码。以后做项目添加新功能最好把里面的原理先弄明白,每一个原理都可能是面试的考点。
4.atomic和nonatomic的区别
nonatomic的内存管理语义是非原子性的,非原子性的操作本来就是线程不安全,而atomic的操作是原子性的,但并不意味着他就是线程安全的,它会增加正确的几率,能够更好的避免线程错误,但仍旧是不安全的。
总结:面试时我把两个关键字的安全性说反了,回家查了一下,atomic也是不安全的,两个关键字都是线程不安全的。
5.copy关键字的作用,NSString是浅拷贝还是深拷贝?
copy分为深拷贝和浅拷贝,浅拷贝是指针的拷贝,深拷贝是内容的拷贝。
NSString是浅拷贝,只有对不可变对象进行copy操作是指针复制(浅复制),其它情况都是内容复制(深复制)。
6.ZFPlayer的底层原理
ZFPlayer底层是基于AVPlayer的。
7.消息转发机制的底层原理
OC的一个对象在发送消息的时候首先在cache里找,如果找不到就在该类的struct objc_method_list列表中去搜索,如果找到则直接调用相关方法的实现,如果没有找到就会通过super_class指针沿着继承树向上去搜索,如果找到就跳转,如果到了继承树的根部(通常为NSObject)还没有找到。那就会调用NSObjec的一个方法doesNotRecognizeSelector:,这样就会报unrecognized selector 错误。
总结:这道题我之前没有看过,让面试官给我讲了一遍,应该牢记,以后可能经常会被问到。
8.NSTimer如何释放?
NSTimer释放不掉循环引用导致内存泄漏的原因:
Timer 添加到 Runloop 的时候,会被 Runloop 强引用,然后 Timer 又会有一个对 Target 的强引用(也就是 self )也就是说 NSTimer 强引用了 self ,导致 self 一直不能被释放掉,所以 self 的 dealloc 方法也一直未被执行。
解决办法:
我们可以造一个假的 target 给 NSTimer 。这个假的 target 类似于一个中间的代理人,它做的唯一的工作就是挺身而出接下了 NSTimer 的强引用。然后在self释放的时候随self一起释放,然后层层解扣,达到在ViewController销毁的时候释放NSTimer。
9.GCD的实际应用
队列和任务的混合搭配
全局任务:本质上是并行队列。
延迟执行任务:dispatch_after
一次执行任务:dispatch_once(单例里面的GCD)
调度组:可以监听全局队列的任务,主队列去执行最后的任务。
主队列:主队列同步(死锁)
总结:之前总结过提到GCD就应该想到任务和队列,即使问的是在实际项目中的应用也应该回答任务和队列的各种组合。用不着回答的太具体,我把项目里的用的实际场景都说了,其实面试官主要想听的就是dispatch_after、dispatch_once这样的关键词。
10.TableView的优化
答:
1.cell的重用
2.缓存高度
3.异步加载
4.尽量少用或不用透明图层
5.减少subviews的数量
6.缓存图片SDWebImage
7.尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示
11.什么时候会发生循环引用?
在-(void)dealloc里invalidate一个NSTimer对象。
这么做会retain cycle的原因:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
这个方法在官方文档里的说明:The timer maintains a strong reference to this object until it (the timer) is invalidated.解释:timer对象会对object拥有一个强引用,如果object是self,那么self就被timer retain,如果timer一直不invalidated,那么dealloc就一直不会被调用。
解决方法:在viewWillDisappear的时候invalidateNSTimer对象。
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[timer invalidate];
}
block中的retain cycle:
block会对用到的对象做一个copy操作,retainCount+1,所以block对用到的对象就有了一个强引用。那么用到的对象是否对block有一个强引用是要看具体条件的。下面就产生了循环引用
[self doSomething:^(BOOL param) {
self.tage = param;
}];
self.doSomethingWithBlock(YES);
12.什么时候会产生内存泄漏?
1.对象之间的循环引用问题
2.Block循环引用
3.delegate循环引用问题
4.NSTimer循环引用
5.WKWebView 造成的内存泄漏
13.Block什么情况会发生循环引用?
答:在一个对象中强引用了一个Block,在该Block中又强引用了该对象,此时就出现了该对象和Block的循环引用。
13.什么是栅栏函数
dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行,并且在barrier函数执行之后,barrier函数之后的操作才会得到执行,该函数需要同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用
箭头就是栅栏函数,我们想要执行完任务一和任务二之后在执行任务三
这次面试被问到的问题很多,我也有很多问题没有答出来,面试官都给我进行了耐心讲解,这次面试的收获很大。