[iOS]iOS面试总结
本人的面试经历,在这里分享给大家,后续会不断更新,大神勿喷,喜欢给个赞,谢谢。
一、NSThread 、NSOperation、GCD的区别
NSThread 比其他两个轻量级,使用简单。但是需要自己管理生命周期、线程同步、加锁、睡眠以及唤醒等。
NSOperation是面向对象的,是对GCD的再次封装。不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。可以方便监听任务状态(开始,执行中,结束等),而GCD则不支持。可以设置同一队列中的任务优先级,也可以设置并发任务的执行顺序。GCD只能设置不同队列的执行优先级。
GCD是基于C语言的底层API,GCD队列任务运行顺序是先进先出(FIOI)也就是先进队列的任务先执行,而NSOperation可以随意调配任务执行顺序。GCD是无法取消加入到队列中的任务的。
二、KVC、KVO、通知中心
KVC也就是key-value-coding,即键值编码,通常是用来给某一个对象的属性进行赋值,例如有人这么一个类,其对外有两个属性,姓名和年龄,我们在创建了一个人p后可以通过点语法直接给p赋值。
KVO,即key-value-observing,利用一个key来找到某个属性并监听其值得改变。当指定的对象的属性被修改后,则对象就会接受到通知。
通知中心(NSNotificationCenter)实际是一种广播机制。把接收到的消息,根据内部的消息转发表,将消息转发给需要的对象。通知是被观察者先主动发出通知,然后观察者注册监听后,再来进行响应。比KVO多了发送通知的一步,但是其优点是监听不局限属性的变化。
三、@property后面可以有哪些修饰符
一类是表示原子性(也就是线程安全)的,有atomic和nonatomic,默认是atomic,acomic也就是线程安全,但是我们一般都用的nonatomic,因为atomic的线程安全开销太大,影响性能,即使需要保证线程安全,我们也可以通过自己的代码控制,而不用atomic。
一类是表示引用计数的,有assign,strong,weak,copy。
assign:assign用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。
weak:对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。
strong:对对象的强引用,会增加对象的引用计数,如果指向了一个空对象,会造成野指针,平常我们用得最多的应该也是strong了。
copy:建立一个引用计数为1的新对象,赋值时对传入值进行一份拷贝,所以使用copy关键字的时候,你将一个对象复制给该属性,该属性并不会持有那个对象,而是会创建一个新对象,并将那个对象的值拷贝给它。而使用copy关键字的对象必须要实现NSCopying协议。
unsafe_unretained:跟 weak 类似,声明一个弱引用,但是当引用计数为 0 时,变量不会自动设置为 nil,现在基本都用weak了。
一类是表示读写权限的,默认是readwrite(可读可写),还有就是readonly,当你希望暴露出来的属性不能被外界修改时就需要申明为readonly。
四、属性的两个修饰词
- @synthesize 的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
- @dynamic 告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成(如,@dynamic var)。
五、block属性用什么修饰?
声明block属性 ,一般用copy修饰,例如:
@property (copy, nonatomic) void (^block)(void);
六、iOS 堆和栈的区别?
堆(先进先出):OC对象存放于堆里面(堆内存要程序员手动回收)
栈(先进后出):非OC对象一般放在栈里面(栈内存会被系统自动回收)
七、Runloop和线程是什么关系?
每条线程都有唯一的一个与之对应的RunLoop对象;主线程的RunLoop已经自动创建,子线程的RunLoop需要主动创建;RunLoop在第一次获取时创建,在线程结束时销毁。
八、Runloop的mode作用是什么?
指定事件在运行循环中的优先级的,线程的运行需要不同的模式,去响应各种不同的事件,去处理不同情境模式。(比如可以优化tableview的时候可以设置UITrackingRunLoopMode下不进行一些操作,比如设置图片等。)
九、使用NSTimer每一段时间执行一些事情时滑动UIScrollView,NSTimer就会暂停,当我们停止滑动以后,NSTimer又会重新恢复的情况。
NSTimer不管用是因为Mode的切换,因为如果我们在主线程使用定时器,此时RunLoop的Mode为kCFRunLoopDefaultMode,即定时器属于kCFRunLoopDefaultMode,那么此时我们滑动ScrollView时,RunLoop的Mode会切换到UITrackingRunLoopMode,因此在主线程的定时器就不在管用了,调用的方法也就不再执行了,当我们停止滑动时,RunLoop的Mode切换回kCFRunLoopDefaultMode,所以NSTimer就又管用了。
十、iOS第一次安装app网络未授权监听网络权限状态
使用CTCellularData检测你的APP的蜂窝移动网络权限是否被关闭。
在需要检测的地方引入#import <CoreTelephony/CTCellularData.h>。
CTCellularData在iOS9之前是私有类,权限设置是iOS10开始的,所以App Store审核没有问题 。具体用法:
CTCellularData *cellularData = [[CTCellularData alloc]init];
CTCellularDataRestrictedState state = cellularData.restrictedState;
switch (state) {
case kCTCellularDataRestricted:
NSLog(@"Restricrted");
break;
case kCTCellularDataNotRestricted:
NSLog(@"Not Restricted");
break;
case kCTCellularDataRestrictedStateUnknown:
NSLog(@"Unknown");
break;
default:
break;
}
十一、让项目中的所有UIViewController打印自己的类名
利用Objective-C的特性给UIViewController添加一个分类;
再通过runtime去截获UIViewController的viewDidLoad方法;
再重新实现viewDidLoad方法,并且添加打印类名方法。
十二、iOS性能优化的方法
用ARC管理内存 。
多使用懒加载。
在正确的地方使用 reuseIdentifier(例如TableView)。
尽量把views设置为不透明(view.opaque=YES)。
避免过于庞大的XIB 。
不要阻塞主线程。
尽量少在ImageViews中调整图片大小,例如: imageView.clipsToBounds = YES; [imageView.layer setCornerRadius:50];这样设置会触发离屏渲染,比较消耗性能。注意:png图片UIImageView处理圆角是不会产生离屏渲染的。
尽量不要使用NSDateFormatter,因为它开销很大,如果一定要使用,建议用延迟加载一个NSDateFormatter
十三、举例说明第三方库的实现原理(这里以SDWebImage为例)
在网络获取图片前,取消并移除当前对象的图片下载线程。
动态关联该图片url(用于图片存缓存的key),一般默认选项有占位图则先显示占位图(其中有个options选项可以不显示或者延时显示占位图等等,稍后再讲)。
使用关联的 key作为路径,在内存中寻找该图片,找不到,再到本地中找,还是找不到,则通过url去服务器中下载。
在上一步中,如果在内存中找到了该图片,则直接返回;如果在本地找到了该图片,则先加载到内存中,再返回;如果需要到服务器拉取,则先把拉取到的图片加载到内存中,再存到本地,最后才返回。
设置对象的图片并显示。
调用类别的方法:
1.从内存(字典)中找图片(当这个图片在本次使用程序的过程中已经被加载过),找到直接使用。
2.从沙盒中找(当这个图片在之前使用程序的过程中被加载过),找到使用,缓存到内存中。
3.从网络上获取,使用,缓存到内存,缓存到沙盒。