技术面试题收集

iOS进阶-09面试题

2020-02-19  本文已影响0人  ricefun

RunTime是什么?

方法的本质是什么?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?

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
上一篇下一篇

猜你喜欢

热点阅读