一、ViewController的生命周期
控制器显示顺序
1、alloc创建对象,分配空间
2、init (initWithNibName )初始化对象,初始化数据
3、loadView : 从nib载入视图,通常这一步不需要去干涉。除非你没有使用xib文件创建。
4、viewDidLoad 载入完成,可以进行自定义数据以及动态创建其他控件。
5、viewWillAppear 视图将要展现时调用
6、viewDidAppear 视图完成显示时调用
控制器消失顺序
1、viewWillDisappear 视图将要消失时调用
2、viewDidDisappear 视图已经消失时调用
3、Dealloc controller被释放时调用
二、UIView和CALayer是什么关系
1、UiView继承自UIResponder,而UIResponder是响应者对象,可以对处理事件的响应以及传递。CALayer是QuartzCore中的类,是一个比较底层的用来绘制内容的类
2、UIView对CALayer封装属性,UIView设置frame,center,bounds等位置信息,其实都是UIView对CALayer的封装
3、UIView是CALayer的代理,UIView持有CALayer的属性,并且是该属性的代理,用来提供一些CALayer行的数据,例如动画和绘制
三、使用drawRect有什么影响?
drawRect方法依赖Core Graphics框架来进行自定义的绘制,缺点:它处理touch事件时,每次按钮被点击后,都会用setNeedsDisPlay进行强制重绘,而且不止一次,每次单点事件触发两次执行,对CPU和内存来说都是欠佳的,
四、什么情况使用weak关键字,相比assign有什么不同?
weak是弱引用,用weak来修饰、描述所引用对象的计数器并不会加1,而且weak会在引用对象被释放的时候自动置为nil,这也就避免了野指针访问坏内存而引起奔溃的情况,另外weak也可以解决循环引用。
拓展:为什么修饰代理使用weak而不是用assign?
assign可用来修饰基本数据类型,也可修饰OC的对象,但如果用assign修饰对象类型指向的是一个强指针,当指向的这个指针释放之后,它仍指向这块内存,必须要手动给置为nil,否则会产生野指针,如果还通过此指针操作那块内存,会导致EXC_BAD_ACCESS错误,调用了已经被释放的内存空间;而weak只能用来修饰OC对象,而且相比assign比较安全,如果指向的对象消失了,那么它会自动置为nil,不会导致野指针。
五 、深拷贝与浅拷贝
举例:https://www.jianshu.com/p/33f175d97b86
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
copy方法:如果是非可扩展类对象,则是浅拷贝。如果是可扩展类对象,则是深拷贝。
mutableCopy方法:无论是可扩展类对象还是不可扩展类对象,都是深拷贝。
六、BAD_ACCESS在什么情况下出现?
访问已经释放对象的成员变量或者发消息。 死循环
七、循环引用
循环引用的实质:多个对象相互之间有强引用,不能释放让系统回收。
如何解决循环引用?
1、避免产生循环引用,通常是将 strong 引用改为 weak 引用。 比如在修饰属性时用weak 在block内调用对象方法时,使用其弱引用,这里可以使用两个宏
define WS(weakSelf) __weak __typeof(&*self)weakSelf = self; // 弱引用
define ST(strongSelf) __strong __typeof(&*self)strongSelf = weakSelf; //使用这个要先声明weakSelf 还可以使用__block来修饰变量 在MRC下,__block不会增加其引用计数,避免了循环引用 在ARC下,__block修饰对象会被强引用,无法避免循环引用,需要手动解除。
代理(delegate)循环引用属于相互循环引用
delegate 是iOS中开发中比较常遇到的循环引用,一般在声明delegate的时候都要使用弱引用 weak,或者assign,当然怎么选择使用assign还是weak,MRC的话只能用assign,在ARC的情况下最好使用weak,因为weak修饰的变量在释放后自动指向nil,防止野指针存在
NSTimer循环引用属于相互循环使用
在控制器内,创建NSTimer作为其属性,由于定时器创建后也会强引用该控制器对象,那么该对象和定时器就相互循环引用了。 如何解决呢? 这里我们可以使用手动断开循环引用: 如果是不重复定时器,在回调方法里将定时器invalidate并置为nil即可。 如果是重复定时器,在合适的位置将其invalidate并置为nil即可
3、block循环引用
由于block会对block中的对象进行持有操作,就相当于持有了其中的对象,而如果此时block中的对象又持有了该block,则会造成循环引用。 解决方案就是使用__weak修饰self即可
在block执行开始时self对象还未被释放,而执行过程中,self被释放了,由于是用weak修饰的,那么weakSelf也被释放了,此时在block里访问weakSelf时,就可能会发生错误(向nil对象发消息并不会崩溃,但也没任何效果)。 对于这种场景,应该在block中对 对象使用__strong修饰,使得在block期间对 对象持有,block执行结束后,解除其持有。
八、网络
1、Http 和 Https 的区别?Https为什么更加安全?
区别
1.HTTPS 需要向机构申请 CA 证书,极少免费。
2.HTTP 属于明文传输,HTTPS基于 SSL 进行加密传输。
3.HTTP 端口号为 80,HTTPS 端口号为 443 。
4.HTTPS 是加密传输,有身份验证的环节,更加安全。
安全
SSL(安全套接层) TLS(传输层安全)
以上两者在传输层之上,对网络连接进行加密处理,保障数据的完整性,更加的安全。
三次握手
1.由客户端向服务端发送 SYN 同步报文。
2.当服务端收到 SYN 同步报文之后,会返回给客户端 SYN 同步报文和 ACK 确认报文。
3.客户端会向服务端发送 ACK 确认报文,此时客户端和服务端的连接正式建立。
建立连接
1.这个时候客户端就可以通过 Http 请求报文,向服务端发送请求
2.服务端接收到客户端的请求之后,向客户端回复Http 响应报文。
四次挥手
当客户端和服务端的连接想要断开的时候,要经历四次挥手的过程,步骤如下:
1.先由客户端向服务端发送 FIN 结束报文。
2.服务端会返回给客户端 ACK 确认报文 。此时,由客户端发起的断开连接已经完成。
3.服务端会发送给客户端 FIN 结束报文 和 ACK 确认报文。
4.客户端会返回 ACK 确认报文到服务端,至此,由服务端方向的断开连接已经完成。
3.TCP 和 UDP的区别
TCP:面向连接、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源)。
UDP:面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快。
九、Runtime
1、初始化时:runtime 会调用 objc_initWeak 函数,初始化一个新的 weak 指针指向对象的地址。
2、添加引用时:objc_initWeak 函数会调用 objc_storeWeak() 函数,objc_storeWeak() 的作用是更新指针指向(指针可能原来指向着其他对象,这时候需要将该 weak 指针与旧对象解除绑定,会调用到 weak_unregister_no_lock),如果指针指向的新对象非空,则创建对应的弱引用表,将 weak 指针与新对象进行绑定,会调用到 weak_register_no_lock。在这个过程中,为了防止多线程中竞争冲突,会有一些锁的操作。
3、释放时:调用clearDeallocating 函数,clearDeallocating 函数首先根据对象地址获取所有 weak 指针地址的数组,然后遍历这个数组把其中的数据设为 nil,最后把这个 entry 从 weak 表中删除,最后清理对象的记录。
OC 的消息机制
OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
objc_msgSend底层有3大阶段,消息发送(当前类、父类中查找)、动态方法解析、消息转发
runtime如何通过selector找到对应的IMP地址?
每一个类对象中都一个对象方法列表(对象方法缓存)
类方法列表是存放在类对象中isa指针指向的元类对象中(类方法缓存)。
方法列表中每个方法结构体中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现。
当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类对象方法列表里查找。
当我们发送一个消息给一个类时,这条消息会在类的Meta Class对象的方法列表里查找。
十、Runloop
1.Runloop 和线程的关系?
一个线程对应一个Runloop。
主线程的默认就有了Runloop。
子线程的Runloop 以懒加载的形式创建。
Runloop 存储在一个全局的可变字典里,线程是 key ,Runloop 是 value。
runloop内部逻辑?
实际上RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。
十一、如何优化Tableview
1.最常用的就是cell的重用, 注册重用标识符
如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell
如果有很多数据的时候,就会堆积很多cell。
如果重用cell,为cell创建一个ID,每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell
2.避免cell的重新布局
cell的布局填充等操作 比较耗时,一般创建时就布局好
如可以将cell单独放到一个自定义类,初始化时就布局好
3.提前计算并缓存cell的属性及内容
4.减少cell中控件的数量
5.不要使用ClearColor,无背景色,透明度也不要设置为0因为渲染耗时比较长
6.使用局部更新 如果只是更新某组的话,使用reloadSection进行局部更
7.加载网络数据,下载图片,使用异步加载,并缓存
8.少使用addView 给cell动态添加view
9.按需加载cell,cell滚动很快时,只加载范围内的cell
10.不要实现无用的代理方法,tableView只遵守两个协议
11.不要做多余的绘制工作。在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制
12.使用正确的数据结构来存储数据。
少不必要的修改
十二、日常如何检查内存泄露?
目前我知道的方式有以下几种
Memory Leaks
Alloctions
Analyse
Debug Memory Graph
MLeaksFinder
泄露的内存主要有以下两种:
Laek Memory 这种是忘记 Release 操作所泄露的内存。
Abandon Memory 这种是循环引用,无法释放掉的内存。
十三、iOS有哪些常见的设计模式?
单例模式:
单例保证了应用程序的生命周期内仅有一个该类的实例对象,而且易于外界访问.在ios sdk中,UIApplication, NSBundle, NSNotificationCenter, NSFileManager, NSUserDefault, NSURLCache等都是单例.
代理模式:
委托Delegate是协议的一种,通过@protocol方式实现,常见的有tableView,textField等。
观察者模式:
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。在iOS中,观察者模式的具体实现有两种: 通知机制(notification)和KVO机制(Key-value Observing)
十四、单例会有什么弊端?
主要优点:
1、提供了对唯一实例的受控访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3、允许可变数目的实例。
主要缺点:
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
·
十五、数据安全及加密
a、对称加密和非对称加密的区别?
1、对称加密又称公开密钥加密,加密和解密都会用到同一个密钥,如果密钥被攻击者获得,此时加密就失去了意义。常见的对称加密算法有DES、3DES、AES、Blowfish、IDEA、RC5、RC6。
2、非对称加密又称共享密钥加密,使用一对非对称的密钥,一把叫做私有密钥,另一把叫做公有密钥;公钥加密只能用私钥来解密,私钥加密只能用公钥来解密。常见的公钥加密算法有:RSA、ElGamal、背包算法、Rabin
b、简述 SSL 加密的过程用了哪些加密方法,为何这么作?
SSL 加密,在过程中实际使用了 对称加密 和 非对称加密 的结合。主要的考虑是先使用 非对称加密 进行连接,这样做是为了避免中间人攻击秘钥被劫持,但是 非对称加密 的效率比较低。所以一旦建立了安全的连接之后,就可以使用轻量的 对称加密。
c、iOS的签名机制是怎么样的
签名机制:先将应用内容通过摘要算法,得到摘要
再用私钥对摘要进行加密得到密文
将源文本、密文、和私钥对应的公钥一并发布
验证流程:
查看公钥是否是私钥方的
然后用公钥对密文进行解密得到摘要
将APP用同样的摘要算法得到摘要,两个摘要进行比对,如果相等那么一切正常