面试技巧攻克-OC高级特性

一、runtime机制

1、什么是runtime?

runtime是OC的重要特性,使得OC语言具有动态的特性,动态指的是,能够在运行时,动态的创建类和对象,进行消息传递和转发。
runtime其实就是将编译期间的决策推迟到运行时再决定,只有在程序运行时,才去检查对象的类型和方法的实现,利用这一特性能够在程序运行时,改变对象的类型和方法的实现。

2、什么是isa指针?有什么作用?

isa是对象中,用来指向它的类的指针,通过isa可以访问这个类的所有父类。
从runtime源码可以了解到,objc_object 和 objc_class两个结构体:
objc_object 代表一个实例变量,objc_class代表一个类,两个结构体中同样都有一个isa指针,但是指向不同。

objc_object指向它的类,也就objc_class
objc_class指向元类(每个类,都有一个元类,用来存放类的类方法列表)

objc_object实例变量,通过isa指针从objc_class中寻找实例变量,实例方法,和协议。如果没有找到,通过super_class指针,到父类中寻找,直到根类为止。

任何NSObject子类的元类都使用NSObject的元类作为自己的所属类,而基类的元类中的isa指针指向它自己,这样就行程一个完美的闭环。

总结:OC中,isa指针是用来维护对象和类之间的关系,并确保对象和类能够通过isa指针找对对应的变量、方法和协议。

3、NSString *obj = [[NSData alloc] init]; 编译时和运行时obj分别是什么类型?

编译时obj为NSString类型,运行时为NSData类型。

4、runtime如何实现weak变量的自动置为nil功能?

runtime在注册和初始化一个类时,当一个属性被修饰为weak时,会将weak变量指向的地址作为value放入一张Hash表中,将weak变量的得知作为key。这样形成一个key-value的键值对,当引用计数变为0的时候,系统通过key-value查找指向weak变量的地址,将变量赋值为nil。

5、能否向编译后的类中增加实例变量?能否向运行时创建的类中添加实例变量?

不能向编译后的勒种增加实例变量,
但可以想运行时创建的类中添加实例变量。

//创建类
        Class myClass = objc_allocateClassPair([NSObject class], "myClass", 0);
        //添加属性
        class_addIvar(myClass, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
        class_addIvar(myClass, "_age", sizeof(NSInteger), log2(sizeof(NSInteger)), @encode(NSInteger));
        //注册类
        objc_registerClassPair(myClass);
        
        id object = [[myClass alloc] init];
        [object setValue:@"Eric" forKey:@"name"];
        [object setValue:@33 forKey:@"age"];
        
        //当类的实例还存在的话,不可以调用objc_disposeClassPair()
        
        object = nil;
        
        //销毁
        objc_disposeClassPair(myClass);

6、简述OC中像一个对象发送消息的整个过程?

消息发送需要对runtime源码有所了解,对OC的对象布局模型有所了解,例如objc_class,objc_object、class_data_bits_t、class_rw_t、class_ro_t等结构体模型。
通过一段程序来进一步说明:

@interface Demo : NSObject

- (void)test;

@end
@implementation Demo

- (void)test{
    NSLog(@"Demo test");
}

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Demo *demo = [[Demo alloc] init];
        [demo test];
    }
    return 0;
}

先给出test方法调用的大致流程:
(1)demo对象通过isa找到它的类对象
(2)在类对象的缓存方法列表中寻找test方法
(3)如果缓存中没有,就到当前类的方法列表中寻找
(4)如果方法列表中没有,就通过superclass到父类的方法列表中寻找
(5)如果父类方法类别也没有,那么就动态解析(Method Resolution)
(6)如果消息解析后还没找到,那么就消息转发(Method Forwarding)
(7)如果还是没找到,程序就崩溃,如果2~6步骤中有一个找到,则返回对应的函数实现(IMP)

在调用test方法中,非常重要的一步是objc_msgSend(),因为runtime涉及到大量的消息传递,为了提供程序的效率,objc_msgSend函数是由汇编语言实现的。汇编语言比较难懂,我们通过一段C语言的伪代码来了解一下内部实现:

id objc_msgSend(id self,SEL _cmd,...){
            Class c = objc_getClass(self);//找到类
            IMP imp = cache_lookup(c,_cmd);//在缓存方法列表中查找imp
            if(!imp){
                imp = class_getMethodImplementation(c, _cmd);//在方法列表中查找imp
            }
            return imp(self,_cmd,...);返回方法实现
        }

通过objc_msgSend源码分析,可以发现,24步骤的实现,那么如果24步骤都没有找到IMP,则会进行5,6两个步骤,通常我们认为5,6两个步骤为消息传递的最后两个挽救过程。那么我们了解一下,这两个过程:

(1)动态方法解析

对象在收到无法解读的消息后,将会调用该类的下列类方法:

//加入我们为调用Demo 的eat方法,但是eat方法没有实现,在IMP没有找到的情况下,会调用下面的方法,
//这时候需要我们手动在这里做处理,这样Demo类就可以成功调用eat方法,而不崩溃。
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    Person *p = [[Person alloc] init];
    [p eat];
    return YES;
}


+ (BOOL)resolveClassMethod:(SEL)sel{
    NSString *Str = NSStringFormSelector(sel);
    if([Str isEqualToString:@"eat"]){
        IMP imp = method_getImplementtation(class_getInstanceMethod(self,@selector(breakfast)));
        class_addMethod(self,@selector(eat),imp,"");
    }
    retun [super resolveInstanceMethod:sel];
}

- (void)breakfast{
    NSlog(@"吃早餐");
}

这样通过动态解析,避免了调用崩溃,从而实现了响应的方法。

(2)消息转发

消息转发分为两步:一个是Fast Forwarding,一个是Nomal Forwarding

如果在动态解析没有成功处理消息,那么runtime会调用如下方法:

- (id)forwardingTargetForSelector:(SEL)aSelector{
    
}

如果Fast Forwarding也没有成功处理,这里唯一能做的就是启用完整的消息转发机制,在使用forwardInvocation方法前,需要先实现methodSignatureForSelector方法,那么runtime会调用如下方法:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    
}

总结:默认情况下,NSObject的forwardingInvocation方法只是简单调用doesNotRecognizeSelector方法,不会转发任何消息。从某种意义上讲,forwardingInvocation类似一个通知中心,可以将所有未知消息都派发出去。

通过上面层层分析:最后总结一下消息传递的整个流程

(1)通过对象的isa指针,找到对象所属的类
(2)在类的缓存方法列表中,查找对应IMP
(3)如果没有找到,在类的方法列表中查找IMP
(4)如过没有找到,通过superclass在父类的缓存方法列表中查找IMP
(5)如果没有找到,在父类的方法列表中查找IMP,通过继承关系,直到根类
(6)如果没有找到,则进行动态解析,调用该类的对应方法进行补救
(7)解析不成功的话,则进行消息转发,分为两步进行补救
(8)如果都失败了,则抛出unrecognizerd selector异常,程序崩溃
(9)如果2~7步骤任何一个找到,则返回IMP

二、内存管理

1、ARC环境下,Autorelease对象什么时候释放?

在不同的@autoreleasepool中,对象的释放时机不同,在iOS中分为两种情况:
(1)如果在iOS工程中不手动添加@autoreleasepool,所有对象由main函数中的@autorelease管理,那么在一个runloop循环结束后,会清理自动释放池中的对象。
(2)如果在iOS工程中手动添加了@autoreleasepool,并在其中创建了对象,那么当对象出了这个@autorelease作用域时会释放。
不管哪种情况,都是通过push操作将对象存入对象,pop操作释放对象。

2、ARC环境下,需不需要手动添加@autoreleasepool?

在ARC环境下,main函数的@autoreleasepool处理了所有自动释放对象,编译器处理所有release对象。一般情况下,不需要手动添加自动释放池。但是在一些特殊情况下,需要我们手动添加。比如:
使用@autoreleasepool来避免内存使用峰值太高的情况。如果你编写的循环中创建了大量的临时对象,使用@autoreleasepool。

3、简述runloop的基本概念和实现原理?

这个有点复杂,慢慢写吧。

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

推荐阅读更多精彩内容