iOS 底层知识总结

2019-12-17  本文已影响0人  wanglei1702

一、OC语法

1、OC对象的本质

1)一个NSObject对象占用多少内存?
A:系统分配16个字节给一个NSObject对象(可以通过C函数malloc_size函数获得,通过查看OC源码,alloc函数,也即allocWithZone:中有判断当字节数小于16时就分配16个字节);而一个NSObject对象仅使用8个字节,用于存放成员变量isa指针,(可以通过runtime 的函数class_getInstanceSize获得)
………………..
2)对象的isa指针指向哪里?
instance对象的isa指向class对象
class对象的isa指向meta-class对象
meta-class对象指向基类的meta-class对象
(以上的“指向”实际是通过isa与ISA_MASK常量进行&位运算的结果)

3)OC的类信息存放在哪里?
成员变量、属性、协议、实例方法等信息存放在类对象中;
类方法存放在元类对象中;
另外,成员变量的具体值存放在实例对象中。

2、KVO

1)iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)
当一个对象的属性被监听时,runtime会动态创建一个新的类,这个新的类是原有类的子类(NSKVONotifying_原有类名),对象的isa会指向这个新类的类对象Class。
当修改对象的属性时,会调用 Foundation 中的 _NSSetXXValueAndNotify 函数,
这个函数会调用willChangeValueForKey:, 然后调用原有类的setter以修改属性值,最后调用didChangeValueForKey:, didChangeValueForKey:内部会触发监听器(Observer)的监听方法-observeValueForKeyPath:ofObject:change:contex:

2)如何手动触发KVO?
手动调用被监听对象的willChangeValueForKey:方法,让后调用didChangeValueForKey:方法,两个方法都需要调用,否则不会触发KVO

3)直接修改成员变量会触发KVO吗?
不会。因为不会走setter方法,就不会走触发KVO的流程

3、KVC

1)通过KVC修改属性会触发KVO么?
会触发KVO,因为 setValueForKey:方法会触发willChangeValueForKey: 和 didChangeValueForKey:方法

2)KVC的赋值和取值时怎样的?原理是什么?
赋值:调用setValue:forkey:方法时,会按 -setKey:,-_setKey: 的顺序查找对象的方法,若查到有实现其中的方法,则调用;若都没有实现,则判断 +accessInstanceVariableDirectly 方法的返回值,若为YES,则按 _key, _isKey, key, isKey的顺序查到对象的实例变量,查找到则赋值。若方法没有实现,+accessInstanceVariableDirectly返回NO或者实例变量都不存在,则会调用valueForUndefinedKey:,并报 NSUnknownKeyException 错误。
取值:调用valueForKey: 会按 -getKey, -key, -isKey, -_key的顺序查找方法,若查到有实现其中的方法,则调用并返回值;若都没有实现,则判断 +accessInstanceVariableDirectly 方法的返回值,若为YES,则按 _key, _isKey, key, isKey的顺序查找成员变量,查找到则将成员变量的值返回。若方法没有实现,+accessInstanceVariableDirectly返回NO或者实例变量都不存在,则会调用valueForUndefinedKey:,并报 NSUnknownKeyException 错误。

4、Category

1)Category的使用场合是什么?
给已有的类扩展属性、方法、和协议;
将一个类按照功能拆分为不同的分类,方便管理和阅读代码

2)Category的实现原理
Category在编译完成后的底层结构是 struct category_t 的结构体,结构体中存放着Category数据(类名,属性列表,对象方法列表,类方法列表,协议列表)
在程序运行的时候,通过runtime将Category数据合并到原类的类对象和原类对象中()

4)Category和Class Extension的区别是什么?
Category是在运行时将其中的数据(如方法等)合并到原类中,
而Class Extension是在编译时就将数据合并到原类中

5)Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?
·有load方法
·load方法是在runtime加载类、分类时调用的
·load方法可以继承,但一般不会主动调用load方法,而是系统自动调用

6)load、initialize方法的区别什么?它们在category中的调用的顺序?以及出现继承时他们之间的调用过程?
①区别:
调用时刻:
load是runtime加载类、分类时调用(只会调用1次);
initialize时类第1次接受到消息时调用,每个类只会initialize一次(但是子类没有实现initialize方法,则父类的initialize方法可能会调用多次)
调用方式:
load时根据函数地址直接调用;而initialize时通过objc_msgSend调用
②调用顺序
load : a.先调用类的load:先编译的类优先调用load;调用子类的load之前,会先调用父类的load
b.后调用分类的load:先编译的分类优先调用
initialize: a.先初始化父类调用父类initialize
b.再初始化子类,调用子类的initialize(若子类没有实现initialize方法,则最终调用的的是父类的intialize方法)

5、Block

1)block 的原理是怎样的?本质是什么?
block 是封装了函数调用和调用环境的 OC 对象

2)__block 的作用是什么?有什么使用注意点?
作用:被 __block 修饰的 auto 变量(即非全局变量、非 static 变量的局部变量),会被包装成一个 OC 对象(结构体),变量的实际值会作为该结构体的一个成员,这样可以解决在 block 中无法修改 auto 变量的问题。
注意点:内存管理相关,__block 修饰的变量在栈上时,block 不会对其强引用;__block 变量被 copy 到堆上时,在 MRC 下,block 不会对其强引用,在 ARC 下,会根据变量的其他修饰关键字进行强引用或弱引用。

3)block的属性修饰词为什么是 copy?使用block有哪些使用注意?
为什么是 copy:block 没有被 copy 之前,是在内存中的栈上或者全局区,生命周期由系统管理;而 copy 之后会被 copy 到堆上,此时 block 生命周期由代码管理。
使用注意:循环引用问题,会导致内存泄漏。

4)block 在修改 NSMutableArray (添加或移除元素),需不需要添加 __block?
不需要。因为对 NSMutableArray 添加或移除元素不需要修改变量的值。

Runtime

1、消息机制

1)讲一下 OC 的消息机制
OC 对象调用方法底层转为 objc_msgSend 函数调用,即给对象(消息接受者)发送一条消息(selector 方法名);
objc_msgSend 函数底层实现大致分为三大阶段:消息发送,动态方法解析,消息转发;
消息发送:通过对象的 isa 指针找到类对象或元类对象(取决于消息接受者是实例对象还是类对象,这里假设是实例对象,类对象的处理类似),依次在类对象中的方法缓存列表和方法列表中查找消息的 selector 方法名,若找到则直接调用方法,否则继续从父类的方法列表中查找,一直找到基类为止;若查找到基类也没有找到方法,则进入动态方法解析阶段;
动态方法解析:实现 -resolveInstanceMethod:(SEL)sel 方法,并调用 runtime 的方法 class_addMethod,为对象动态地添加一个方法,然后再次进入消息发送阶段,并标记已经尝试过动态方法解析;若添加了方法,则直接调用;若没有添加,则进入消息转发阶段;
消息转发:实现 -forwardingTargetForSelector: 方法,返回一个新的对象做为消息接受者,并调用 objc_msgSend 方法;若没有返回新的对象而是返回 nil,则会调用 -methodSignatureForSelctor: 方法,若此方法返回一个方法签名,则进入 -forwardInvocation: 方法,可以做任何处理;若返回 nil,则会报经典错误 unrecognized selector sent to instance xxxxx

2)消息转发机制流程
同问题1)

3)什么是Runtime?平时项目中有用过么?
Runtime:OC 是一门动态性很强的语言,很多操作允许推迟到程序运行时再执行;
OC 的动态性是由 Runtime 来实现的,Runtime 是一套 C 语言的 API,封装了很多动态性相关的函数;
平时编写的 OC 代码,底层都是转换为了 Runtime 的 API 进行调用。
用途:给分类添加属性实现(关联对象 AssociateObject);
方法交换(method_exchange);
遍历类的所有成员变量(归档解档,获取私有成员如textField._placeholderLabel,字典转模型)
利用消息转发机制,监测方法找不到的问题;
...

上一篇下一篇

猜你喜欢

热点阅读