iOS-OC底层原理

1.OC对象的本质

1.1一个NSObject对象占用多少内存?

    系统默认分配16个字节给NSObject对象(通过malloc_size函数获得),但NSObject对象只使用了8个字节的空间(64bit环境下,通过class_getInstaneSize获得),8个字节用来存放isa指针变量,在编译是oc对象会转换成C语言中的结构体。

1.2OC对象的分类

    OC对象分为实例对象(instance)、类对象(class)、元类对象(meta class),

    实例对象是通过alloc init生成的对象,存储isa指针和成员变量的值。类对象通过[object1 class]、object_getClass(object1)、[NSObject class]获取的对象,存储isa指针、superClass指针、对象方法、成员变量的信息、类的属性信息(@property)、协议信息。元类对象是通过类对象调用object_getClass(object1)获取得到的,主要存储类的 存储isa指针、superClass指针、类方法等信息。

1.3 Class objc_getClass(const char *name) 和Class object_getClass(obj)函数的区别

    Class objc_getClass(const char *name) 传入字符串类名,返回对应的类对象。

    Class object_getClass(obj)传入的可能是instance、class、metaClass对象,返回值如果传入的是instance对象,返回class对象;如果传入的是class对象,返回metaClass对象;如果传入的是metaClass对象,返回NSObject根源类(基类)的metaClass对象。

1.4isa和superClass的总结

instance的isa指向class对象

class对象的isa指向metaClass对象

metaClass对象的isa指向基类对象

class的superClass指向父类的class,如果没有父类,superClass则为nil

    metaClass的superClass指向父类的metaClass,父类的metaClass指向基类的metaClass,基类的metaClass的superClass指向基类的class对象

    instance调用对象方法的轨迹:instance通过isa找到自己的class对象,如果在class对象方法列表找不到对象方法,就通过superClass查找父类的类对象方法,如果都找不到,会报错:unrecognized selector send to XXX。

    class调用类方法的轨迹:class对象通过isa寻找自己的metaClass,如果在metaClass的类对象方法列表中找不到对应的类方法,就通过superClass找父类的metaClass的类方法,如果都找不到,会报错:unrecognized selector send to XXX。

2.1KVO实现原理

2.1iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

    利用Runtime动态生成一个继承自该对象的子类,并且让instance对象的isa指向这个全新的子类。当修改该对象的属性时(调用成员变量的set方法),会调用Foundation的NSSetXXXValueAndNotify函数。             

    通过调用willChangeValueForKey: ,父类原来的setter,didChangeValueForKey:函数,触发内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:)

2.2如何手动触发KVO?

    手动调用willChangeValueForKey:和didChangeValueForKey会触发KVO

2.3直接修改成员变量会触发KVO么?

    在不进行手动触发KVO的前提下,直接修改成员变量,不会触发KVO

2.4通过KVC修改属性会触发KVO么?

    在通过KVO直接监听instance对象时,不管通过setValueForKey:还是直接修改对象的成员变量,都会触发KVO,原因是KVC内部会调用willChangeForKey和didChangeForKey方法。

2.5KVC的赋值和取值过程是怎样的?原理是什么?

setValue:forKey:的原理

    首先会查找setKey:、_setKey: (按顺序查找),如果有直接调用,如果没有,先查看accessInstanceVariablesDirectly方法,如果可以访问会按照 _key、_isKey、key、iskey的顺序查找成员变量,找到直接赋值,未找到报错NSUnkonwKeyException错误。

valueForKey:的原理

    kvc取值按照 getKey、key、iskey、_key 顺序查找方法,存在直接调用,没找到同样,先查看accessInstanceVariablesDirectly方法,如果可以访问会按照 _key、_isKey、key、iskey的顺序查找成员变量,找到直接赋值,未找到报错NSUnkonwKeyException错误

3.1 Category

3.1Category的使用场合是什么?

1.需求变化,需要添加新的方法

2.原有类代码结构复杂,通过category分化模块

3.多人协作开发,用分类区分代码模块

4.扩充系统基础类,比如给NSString添加新方法

5.覆盖原有类方法

3.2Category的实现原理

    category编译之后底层是struct category_t结构体结构,里面存储分类的对象方法、类方法、属性、协议信息等。在程序运行时,runtime会将category的数据,合并到原有类的信息中,实例方法会存储在对应的类对象中,类方法会存储在元类对象中。注意:多个category和原有类同时存在同名函数,category会覆盖原有类的同名方法,不同category之间调用方法取决于文件编译顺序,最后进行编译的文件的方法,优先级最高,会优先调用该文件的同名函数方法。

3.3Category和Class Extension的区别是什么?

Class Extension在编译的时候,类扩展的就已经包含在类信息中(编译时)

category是在运行时,利用runtime机制,才会将数据合并到类信息中(运行时)

3.4load方法的加载原理

    load方法在类、分类加载进内存的时候调用(运行时),在程序运行的过程中只调用一次。调用顺序:先调用类方法的load,再调用分类的load,不同类、分类文件之间加载load方法的顺序按照文件编译的顺序调用。在加载子类的load方法时,必须先调用父类的load方法。

    load方法是根据方法地址直接调用,并不是经过objc_msgsend方法来调用。

3.4-2 initialize方法的调用原理

initialize方法在类第一次接收消息的时候调用,如果这个类在程序运行时没有被创建过,则不会被调用。调用顺序:先调用父类的initialize,再调用子类的initialize,每个类只会被初始化一次。如果子类没有实现initialize方法,通过消息机制,会调用父类实现的initialize方法。+initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点

如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)

如果分类实现了+initialize,就覆盖类本身的+initialize调用。

3.4.1Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?

有load方法

load方法在runtime加载类、分类的时候调用

load方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用

3.4.2load、initialize方法的区别什么?它们在category中的调用的顺序?以及出现继承时他们之间的调用过程?

load、initialize方法的区别什么?

1.调用方式

1> load是根据函数地址直接调用

2> initialize是通过objc_msgSend调用

2.调用时刻

1> load是runtime加载类、分类的时候调用(只会调用1次)

2> initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)

load、initialize的调用顺序?

1.load

1> 先调用类的load

a) 先编译的类,优先调用load

b) 调用子类的load之前,会先调用父类的load

2> 再调用分类的load

a) 先编译的分类,优先调用load

2.initialize

1> 先初始化父类

2> 再初始化子类(可能最终调用的是父类的initialize方法)


4.block的原理是怎样的?本质是什么?

    封装了函数调用以及调用环境的OC对象,本质还是一个OC对象。

4.1block的变量捕获

为了保证block能访问外部变量,所以block有个变量捕获机制。

1、局部变量:对于auto类型的变量,block捕获的变量的方式是值传递。对于static修饰的变量,因为static修饰的变量在内存中只有一份,内存地址不会发生变化,所以访问变量的方式是指针传递。

2、全局变量,因为在任何地方都可以访问全局变量,所以不需要捕获变量,直接进行访问就行。

4.2 block的类型

    block分为NSGlobalBlock、NSStackBlock、NSMallocBlock三类。

    应用程序的内存分成:代码区、数据区、堆、栈四个部分。

    NSGlobalBlock是存储在数据区的block,没有访问auto类型的变量。NSStackBlock是存储在栈区的block,访问了auto类型的变量。NSMallocBlock是存储在堆区的block,将NSStackBlock进行copy操作就变成了NSMallocBlock。

4.1__block的作用是什么?有什么使用注意点?

    __block可以用于解决block内部无法修改auto变量值的问题,其本质是编译器会将__block变量包装成一个对象。注意点:__block不能修饰全局变量、静态变量(static)。

4.2 block的属性修饰词为什么是copy?使用block有哪些使用注意?

    block一旦没有进行copy操作,就不会在堆上,在ARC下,使用strong和copy本质上没有什么区别,使用strong也可以将block copy到堆上。

使用注意:循环引用问题

4.3如何解决block的循环引用问题?

在ARC下:

1、可以使用__weak typeof(self) weakSelf = self,将对象进行弱引用。

2.使用unsife_unretain修饰词进行弱引用。

    相同点是__weak、unsife_unretain都可以改变强引用变成弱引用,不同点是unsafe_unretain是不安全的,用__weak进行修饰,变量被释放后指针会自动被修改成nil,unsafe_unretain则不会,再次访问该对象会报 野指针错误。

3.可以使用__block解决循环引用,核心操作是 在block内部 将对象置成nil,person=nil,并且需要调用该block,block()。缺点是:如果不主动调用block(),则该对象不会被释放,造成内存泄漏。

综上:在ARC下推荐使用___weak。

在MRC下:可以使用unsife_unretain和block解决循环引用。


4.4 block在修改NSMutableArray,需不需要添加__block?

    不需要,如果是对NSMutableArray 进行addObject等增删操作,不需要调用__block。如果要进行array=nil,则需要添加__block。

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

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 面试题的答案都是抛砖引玉,具体细节还得深入了解iOS底层原理复制代码 1、一个NSObject对象占用多少内存? ...
    大豆豆_小豆豆阅读 348评论 0 0
  • 1、Objective-C的本质 Objective-C代码,底层实现其实都是C\C++代码。Objective-...
    学习天亦阅读 638评论 0 0
  • ————“勇气读书会”第三季第12篇 第八章第四节,胜可知不可为。 意思是善于打仗的人,先要做到自己...
    潇湘雨默阅读 180评论 0 4
  • 前几日读秋大的一篇文章,里面提到了这个观点,确实得以深思,今日就说说关于生存与生活的那些事! 生存的概念是什...
    阿毅阅读 285评论 0 1