1.简述KVC和KVO,其中KVO实现原理?
KVC : 键值编码(Key-Value Coding),它是一种通过key值访问类属性的机制,而不是通过setter/getter方法访问。其中 KVC 原理:当调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key时,KVC底层的执行机制如下:
首先搜索对应属性的setter方法
如果没有找到属性的setter方法,则会检查+ (BOOL)accessInstanceVariablesDirectly方法是否返回了YES(该方法默认返回YES),如果返回了YES, 则KVC机制会搜索类中是否存在该属性的成员变量,也就是_属性名,存在则对该成员变量赋值。搜索成员变量名的顺序是 _key,_isKey,key,isKey。
另外我们也可以通过重写+ (BOOL)accessInstanceVariablesDirectly方法返回NO,这个时候KVC机制就会调用 - (void)setValue:(id)value forUndefinedKey:(NSString *)key。
如果没有找到成员变量,调用 - (void)setValue:(id)value forUndefinedKey:(NSString *)key。
2.Block实现原理;堆上和栈上的数据如何同步?
block本质上也是一个oc对象,他内部也有一个isa指针。block是封装了函数调用以及函数调用环境的OC对象。结构体,在栈上的情况, Block中的指针只是指向栈上的__block变量, 而当Block/__block变量被copy到堆上以后, 堆上Block会持有堆上__block变量. 而堆上的Block再次被调用copy时, 只是Block的引用计数+1而已, 而__block变量如果被多个堆上Block持有也只涉及到引用记数的变化. 一旦Block/__block变量的引用计数为0, 就会自动从堆上释放内存.这里Block/__block变量在堆上的内存管理与Objective-C对象完全一致.
3.推送如何实现的?
1.由App向iOS设备发送一个注册通知,用户需要同意系统发送推送。
2.iOS应用向APNS远程推送服务器发送App的Bundle Id和设备的UDID。
3.APNS根据设备的UDID和App的Bundle Id生成deviceToken再发回给App。
4.App再将deviceToken发送给远程推送服务器(自己的服务器), 由服务器保存在数据库中。
5.当自己的服务器想发送推送时, 在远程推送服务器中输入要发送的消息并选择发给哪些用户的deviceToken,由远程推送服务器发送给APNS。
6.APNS根据deviceToken发送给对应的用户。
4.简述weak的实现原理;
weak 关键字的作用弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为 nil;
weak是有Runtime维护的weak表;3.weak释放为nil过程
weak被释放为nil,需要对对象整个释放过程了解,如下是对象释放的整体流程:
1、调用objc_release
2、因为对象的引用计数为0,所以执行dealloc
3、在dealloc中,调用了_objc_rootDealloc函数
4、在_objc_rootDealloc中,调用了object_dispose函数
5、调用objc_destructInstance
6、最后调用objc_clear_deallocating。
对象准备释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
其实Weak表是一个hash(哈希)表,然后里面的key是指向对象的地址,Value是Weak指针的地址的数组
总结
weak是Runtime维护了一个hash(哈希)表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。
5. TCP 和 UDP 的区别
TCP 为传输控制层协议。这种协议提供面向连接的、可靠的、点到点的通信;
UDP 为用户数据报协议。这种协议提供非连接的、不可靠的、点到多的通信但是比 TCP 快。
6. TCP 的三次握手
第一次握手:客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态 ,等待服务器确认;
第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 syn 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN+RECV 状态;
第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=k+1),此时发送完毕,客户端和服务端进入 ESTABLISHED 状态,完成三次握手
7. 属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用
(1) readwrite 是可读可写特性;需要生成getter方法和setter方法时
(2) readonly 是只读特性 只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变
(3) assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;
(4) retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;
(5) copy 表示赋值特性,setter方法将传入对象复制一份;需要完全一份新的变量时。
(6) nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic
8. @property 的本质是什么,ivar、getter、setter 是如何生成并添加到这个类中的
在普通 OC 对象中,@property 就是编译器自动帮我们生成一个成员变量(ivar)和它的 setter 和 getter 方法,它大概生成了五个东西:
1. objc_ivar_$类名$属性名称 该属性的偏移量
2. setter 与 getter 方法对应的实现函数
3. ivar_list 成员变量列表
4. method_list 方法列表
5. prop_list 属性列表
也就是说我们每次增加一个属性,系统就会在成员变量列表中添加一个成员变量的描述,在方法列表中添加 setter 与 getter 方法的描述,在属性列表中增加一个属性描述,然后计算该属性在对象中的偏移量,然后生成 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量开始复制,在 getter 中从偏移量开始取值,为了能够读取正确的自己数,系统对对象偏移量的指针类型进行了类型强转。
9. @synthesize 和 @dynamic 分别有什么作用
@property 有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize 和 @dynamic 都没写,那么默认的就是 @syntheszie var = _var;
@synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
@dynamic 告诉编译器,属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter 方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var =someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
10. get 与 post 的区别
get 是向服务器索取数据的一种请求,post 是向服务器提交数据的一种请求;
get 没有请求体,post 有请求体;
get 使用 url 或 cookie 传参,而 post 将数据放在 body 中;
get 请求的数据会暴露在地址栏中,而 post 不会,所以 post 比 get 更安全;
get 请求对 url 长度有限制,而 post 请求对 url 理论上没有限制,但实际上,各个服务器会规定对 post 提交数据大小进行限制。
11. 描述下 SDWebImage 里面给 UIImageView 加载图片的逻辑
SDWebImage 中为 UIImageView 提供了一个分类 UIImageView+WebCache.h。这个分类中有一个最常用的方法:sd_setImageWithURL:placeholderImage: 会在真实图片出来前先显示占位图片,当真实图片被加载出来后再替换占位图片。
加载过程大致如下:
首先会在 SDWebImageCache 中寻找图片是否有对应的缓存,它会以 url 作为数据索引先在内存中寻找是否有对应的缓存,如果缓存未找到就会利用通过 MD5 处理过的 key 来继续在磁盘中查询对应的数据,如果找到了,就会把磁盘中的数据加载到内存中,并将图片显示出来,如果在内存中和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片,下载后的图片会加载到缓存中,并写入磁盘中。整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来。
原理:
从内存中找到图片,找到直接使用;
从沙盒中找,找到使用,缓存到内存中;
从网络上获取,使用,缓存到内存,缓存到沙盒。