第一部分: OC语法

前言:

我们直接用面试题的方式来回答和解读oc语法的底层原理。看下面的面试题:

有关oc面相对象的:

1,面向对象编程OOP是什么意思?谈谈你对OOP的理解。
2,一个nsobject占用多少内存空间?
3,对象的isa指针和superclass指向哪里?
4,oc对象的类信息存放在哪?
5,oc对象的本质是什么?
6,谈谈你对isa指针的了解?

KVO有关的:

1,oc是如何实现对一个对象的KVO?
2,如何手动触发KVO?
3,直接修改成员变量会触发KVO吗?

KVC相关的:

1,KVC的实现原理是怎样的?
2,通过KVC修改属性会触发KVO吗?

Category分类相关的:

1,Caregory是什么?什么场合使用?
2,Caregory的实现原理?
3,Catefory和Class Extension的区别是什么?
4,Category中有load方法吗?load方法和intialize方法的区别是什么?

关联对象相关的 :

1,关联对象常用的api有哪些?
2,关联对象的底层是怎么实现的?

Block相关(写了另一篇来讲了)

链接:https://www.jianshu.com/p/eed13c33c8bc

答案和详解

一, OC对象的本质

面试题1:面向对象编程OOP是什么意思?谈谈你对OOP的理解。
面试题2: 一个nsobject对象占用多少内存空间?

答: 系统给nsobject分配了16个字节的空间(可以通过#import<malloc/malloc.h>框架下的malloc_size()方法来获取);
但实际上在64bit环境下nsobject对象内部只使用了8字节的空间(可以通过#import<objc/runtime.h>的class_getInstanceSize()方法来获取).
以上是一个没有属性和成员变量的对象所占用的字节, 通过上面两个方法, 我们还可以发现, 对于拥有多个成员变量和属性的对象, 系统给其分配的内存空间是以16的倍数增长的, 而实际内部占用的空间则不一定是16的倍数.

面试题3: oc对象的分类有几种? isa指针是怎样的?

答: 三种, 分别是:
instance对象(实例对象);
class对象(类对象);
meta-class对象(元类对象).
下面是他们的isa指针指向和superclass指针的指向。其中需要注意的是:基类元对象的isa指针指向自己;基类元对象的superclass指针指向基类的class对象,而基类对象的superclass指针指向nil。


isa指向.png
面试题4:oc对象的本质是什么?

OC对象的本质是一个结构体。通过将OC的文件编译成cpp文件之后可以看到,一个OC的类编译之后就是一个cpp的结构体,结构是这样的:


类的本质.png

对于自定义的类,例如GQStudent类,编译后的结构体跟上面是一样的,只是编译后的结构体的名称一般都是student_IMPL{}.

面试题5:oc对象的类信息存放在哪?

根据上面的两个题,那么这个问题就很好回答了。类对象的信息是存放在objc_class结构体的bits结构体中;并且通过FAST_DATA_MASK掩码可以得到bits结构体地址,在bits中的ro结构体里面存放着oc对象的类信息。ro包括的信息有:instanceSize表示对象占用内存空间大小,name类名,ivars成员变量列表等等。

面试题6:谈谈你对isa指针的了解?

在64bits之前,isa指针指示单纯的指针,里面存放着所指向的对象地址。在64bits之后,isa指针被优化成tagged pointer,里面不仅能存放地址,也能存放相对较小的number string等对象,而不需要为这样的对象开辟新的内存地址,用来减小内存开销和调用效率。并且在64bit之后,需要通过ISA_MASK掩码才能获得真正的isa地址

二, KVO有关的

面试题1: oc是如何实现对一个对象的KVO?

当我们对一个instance对象进行KVO的时候,runtime的API会动态生成一个全新的中间对象,当修改instance中的属性时,会调用Fundation的NSSet**ValueAndNotify{
[self willChangeValueForkey:@“”];
[原来的instance的setter方法实现];
[self didChageValueForKey:@“”];
},并且会触发内部的observe监听器的监听方法(observeValueForKeyPath:ofObject:change:context:)以实现对对象的监听。
以MJPerson为例,如图所示:
原来的MJPerson的instance对象和class对象之间新建一个中间对象NSKVONotifying_MJPerson对象,并且把instance的isa指向NSKVONotifying_MJPerson,把NSKVONotifying_MJPerson的superclass指针指向原来的class对象。NSKVONotifying_MJPerson中对setter方法进行了重写,重写的原理是在原来的setter方法赋值之前调用[self willChangeValueForKey:@""]和赋值之后调用[self didChangeValueForKey:@""]. 并且重写后的方法会触发NSFundation_NSSet
ValueAndNotify方法发出通知。所以这是实现的原理。

KVO原理.png

面试题 2:如何手动触发KVO?

手动的调用willChangeValueForKey:或者didChangeValueForKey:方法都能触发KVO。

面试题 3:直接接修改成员变量会触发KVO吗?

不会,因为不经过上面1说的那个过程。

三, KVC相关的:

面试题 1:KVC的实现原理是怎样的?

KVC全程是Key_Value Coding也就是俗称的“键值编码”,可以通过key来访问某个属性。常见的KVC的API接口有:


kvc接口.png

当我们调用setValue:forKey:方法时,会先按照次序调用setKey:和_setKey:方法修改属性值,而如果调用不成功则会调用accessInstanceVariableDirectly:方法,如果返回YES则按照次序调用_key:和 _isKey: 和 key:和 _isKey:方法获取属性并直接赋值;而如果accessInstanceVariableDirectly:返回YES后调用了前面四个方法之后没找到属性值,或者accessInstanceVariableDirectly返回了NO,则调用setValue:forUndefineKey:方法并抛出异常NSunknownKeyException。下图是调用的过程图:


kvc赋值.png

当我们调用valueForKey:方法时候,过程也是类似的。会首先依次调用getKey:→key:→isKey:→_key方法,找到了则直接调用获取属性;找不到则会调用accessInstanceVariablesDirectly如果返回了YES,则依次调用_key:→ _isKey:→ key:→ isKey:方法找到了则直接调用取值。如果上面找不到或者accessInstanceVariablesDirectly直接返回NO,则调用setValue:forUndefineKey:方法并抛出异常NSunknownKeyException。下图是调用的过程图:
kvc取值.png
面试题 2:通过KVC修改属性会触发KVO吗?

会。因为KVO调用过程中会调用setter方法,进而触发KVO。

四,Category(也叫分类)相关的:

面试题 1:Caregory是什么?什么场合使用?

category也叫分类,是runtime支持实现的对类进行动态添加拓展, 是oc语法的一种。
分类使用的场合主要有:
1,降低单个类的体积,降低耦合性,可以多人开发;
2,可以对系统的类进行拓展;
3,可以模拟多继承;
4,可以用来对静态库方法进行公开。

面试题 2:Caregory的实现原理?

category在编译后是一个结构体category_t,里面存放着分类的类方法、方法、协议信息等,但是这个结构体里并没有属性列表,所以不能往分类里添加属性。在程序运行时候,runtime会将一个类的分类动态的合并到类信息里面(包括类对象和元类对象)。并且后编译的category会排在class的methods列表前面,所以当一个类有多个category时,后面编译的category的类方法和方法等会优先被调用。但这不是覆盖,而是优先调用。

面试题 3:Catefory和Class Extension的区别是什么?

category和class extension是两个不同的概念,都是oc语法中的一种,只是中文叫法有点类似。
它们的区别主要有:
1,category一般只能用来对类进行方法的扩展,而不能添加成员变量属性变量;而class extension可以添加属性和方法并且会自动生成setter和getter方法。
2,category中的方法是运行时合并到原来的类信息之中,而class extension中的属性和方法等是编译时候就添加到原来的类信息之中了。所以当category中声明的方法没有实现时候,编译时候也不会被提醒,而class extension就会报警。
3,class extension没有独立的.m文件,是需要在原来的类的.m文件中进行实现的,也就是需要有原来的类代码,需要依托源代码实现。而category有独立的.m文件,可以在.m中实现方法。

面试题 4:Category中有load方法吗?load方法什么时候调用?load方法可以被继承吗?load方法和intialize方法的区别是什么?

category中是有load方法的。+load方法是在runtime加载类和分类的时候调用,并且在整个程序运行过程中知知调用一次。load方法可以被继承但实际开发中我们都最好不要主动调用laod方法,让系统自动调用。而且调用的次序分别是:
先调用类的+load方法(其中,是按照编译顺序来调用的,先编译先调用,而且调用子类的load方法之前会先调用父类的load);
然后再调用分类的laod(其中,分类是按照编译顺序来调用的,先编译的会先被调用)。
而且由上面的顺序可以看出,load方法是通过方法地址直接调用的,而不是通过objc_msgSend方法来调用。

intialize方法是在类第一次接收到信息时候调用。并且是先调用父类的intialize方法再调用子类的intialize方法(其实就是:先初始化父类在初始化子类,并且每个类只会初始化一次)。
intialize方法跟load方法最大的区别是调用不同,intialize方法是通过消息objc_msgSend方法调用的,而load方法是通过内存地址直接调用的。所以他们有以下各自的特点:
当子类的intialize方法没有实现的时候,就会调用父类的intialize方法(所以父类的intialize方法有可能会被多次调用);
如果分类中实现了intialize方法,则会覆盖父类的intialize方法的调用。

面试题 5:如何给Category添加属性?

category的底层结构限制了无法给分类添加属性。但是可以通过关联对象来间接地实现给分类添加属性。

五,关联对象(associatedObject)相关的:

面试题 1:关联对象常用的api有哪些?

常用的有三个接口:


image.png

其中,对于key,除了可以使用属性名来作为key以外,还可以是使用方法签名来作为key这样可以较好的保证其唯一性。例如可以像下面这样设置key:


image.png

然后,关于objc_AssociationPolicy可以参考下面的表格:
image.png
面试题 1:关联对象的实现原理?

关联对象技术的核心对象有以下四个:
AssosiationManager;
AssosiationHashMap;
ObjectAssosiationMap;
ObjectAssosiation.
他们的之间的关系是这样的:


image.png

原理是这样的:关联对象并不是存储在被关联对象本身的内存中,而是存储在一个由runtime来保持的全局的统一的AssosiationManager对象中;这个对象持有一个AssosiationHashMap,在AssosiationHashMap里面存储着被关联对象的key以及被关联的属性ObjectAssosiationMap,而ObjectAssosiationMap中就存储着要关联的属性。
所以,当我们往一个对象里添加关联对象的时候,AssosiationManager就会往自己的AssosiationHashMap中添加一个键值对,这个键值对里就存着需要添加的属性信息。当我们要移除关联对象时候,除了可以调用移除方法以外,还可以通过设置nil来移除。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容