iOS进阶-09面试题

作者: ricefun | 来源:发表于2020-02-19 17:54 被阅读0次

    RunTime是什么?

    • Runtime是由C、C++、汇编实现的一套API,为OC加入了面向对象、运行时功能
    • 运行时是指将数据类型的确定由编译时推迟到运行时;
    • 平时编写的OC代码,在程序运行过程中,其实最终会转化成Runtime的C语言代码,Runtime是OC的幕后工作者

    方法的本质是什么?Sel、Imp又是什么?两者间的关系?

    方法的本质:发送消息,发送消息会有以下几个过程
    1、快速查找:(objc_msgSend) -- cache_t缓存消息
    2、慢速查找:递归自己|父类 -- lookUpimpOrForward
    3、查找不到消息:动态方法解析 -- resolveInstanceMethod
    4、消息快速转发:-- forwardingTargetForSelector
    5、消息慢速转发:-- methodSignatureForSelecot & forwardInvocation

    sel是方法编号 - 在read_images期间就编译进入了内存
    imp就是函数的实现指针,找imp就是找函数的过程
    sel相当于是书本的目录title
    imp 就是书本的页码
    查找具体函数就是根据title(sel)查找页码(imp)的过程,最后翻到具体的页面

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

    1、不能向编译后得到的类增加实例变量
    2、只要类没有注册到内存,还是可以添加的

    原因:编译好的实例变量储存在ro中,一旦编译完成,内结构就会确定,无法修改;但是可以添加属性和方法

    [self class] 和 [super class]

    #import "Student.h"
    
    @implementation Student
    - (instancetype)init {
        if (self = [super init]) {
            NSLog(@"%@",NSStringFromClass([Student class]));
            NSLog(@"%@",NSStringFromClass([super  class]));
        }
        return self;
    }
    
    @end
    
    打印结果
    Student
    Student
    

    解析:


    截屏2020-02-1915.35.33.png

    [self class]就是发送消息objc_msgSend,消息接受者是self,方法编号:class
    [super class]就是发送消息objc_msgSendSuper,消息接受者还是self,方法编号:class,只是objc_msgSendSuper会更快,直接跳过self的查找

    weak的是如何实现自动置为nil?

    • 首先系统会维护一张散列表(sidetable),这张表中又包含了弱引用表(weak_table),当然还有其他表(eg:引用计数表)
    • 当一个属性被设置成weak时,weak_table表中会查找当内部有没有该对象的弱引用数组(weak_entry数组),如果有就直接插入这个属性到这个weak_entry数组,没有就先创建weak_entry数组再插入
    • 当对象被释放时(delloc),会通过对象指针去查找weak_table没有该对象的weak_entry数组,有的话遍历weak_entry数组,将内部的对象置为nil;最后将这个weak_entry数组remove
    void 
    weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
    {
        objc_object *referent = (objc_object *)referent_id;
    
        weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
        ...
        ...  
          for (size_t i = 0; i < count; ++i) {
            objc_object **referrer = referrers[i];
            if (referrer) {
                if (*referrer == referent) {
                    *referrer = nil;
                }
                else if (*referrer) {
                    _objc_inform("__weak variable at %p holds %p instead of %p. "
                                 "This is probably incorrect use of "
                                 "objc_storeWeak() and objc_loadWeak(). "
                                 "Break on objc_weak_error to debug.\n", 
                                 referrer, (void*)*referrer, (void*)referent);
                    objc_weak_error();
                }
            }
        }
        
        weak_entry_remove(weak_table, entry);
    }
    

    strong是如何工作的?

    strong内部就是向当前对象发送一个retain消息 截屏2020-02-1916.15.57.png

    Method-Swizzling

    #import "LGRuntimeTool.h"
    #import <objc/runtime.h>
    
    @implementation LGRuntimeTool
    + (void)lg_bestMethodSwizzlingWithClass:(Class)cls oriSEL:(SEL)oriSEL swizzledSEL:(SEL)swizzledSEL{
        
        if (!cls) NSLog(@"传入的交换类不能为空");
        
        Method oriMethod = class_getInstanceMethod(cls, oriSEL);
        Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);
        
        if (!oriMethod) {//判断原始方法有没有实现,如果没有那就添加一个空的block实现,你可以在block中,进行bug上传
            // 在oriMethod为nil时,替换后将swizzledSEL复制一个不做任何事的空实现,代码如下:
            class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
            method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){
                //这里面进行bug上传
            }));
        }
        
        // 一般交换方法: 交换自己有的方法 -- 走下面 因为自己有意味添加方法失败
        // 交换自己没有实现的方法:
        //   首先第一步:会先尝试给自己添加要交换的方法 :personInstanceMethod (SEL) -> swiMethod(IMP)
        //   然后再将父类的IMP给swizzle  personInstanceMethod(imp) -> swizzledSEL
        //oriSEL:personInstanceMethod
        //防止子类没有实现,而父类实现,相当于交换了父类的方法
        BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
        if (didAddMethod) {
            class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
        }else{
            method_exchangeImplementations(oriMethod, swiMethod);
        }
    }
    @end
    

    使用

    #import "LGStudent+LG.h"
    #import "LGRuntimeTool.h"
    #import <objc/runtime.h>
    
    @implementation LGStudent (LG)
    
    + (void)load{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{//只交换一次防止在其他地方手动(load方法)导致方法又exchange回来
            [LGRuntimeTool lg_betterMethodSwizzlingWithClass:self oriSEL:@selector(helloword) swizzledSEL:@selector(lg_studentInstanceMethod)];
        });
    }
    
    - (void)lg_studentInstanceMethod{
        NSLog(@"LGStudent分类添加的lg对象方法:%s",__func__);
        [self lg_studentInstanceMethod];
    }
    @end
    
    

    指针调用实例方法&内存偏移

    截屏2020-02-1917.36.49.png

    相关文章

      网友评论

        本文标题:iOS进阶-09面试题

        本文链接:https://www.haomeiwen.com/subject/jyeafhtx.html