iOS

iOS 面试题分析(二)

2021-08-02  本文已影响0人  俊而不逊

1.回顾

在之前的博客中,对OC底层进行了一系列的源码的探索分析,上一篇博客也对一些面试题进行了回答和分析,本篇博客继续面试题分析!

面试题分析

2. iOS面试题分析

2.1 ⽅法的本质?sel是什么?IMP是什么?两者之间的关系⼜是什么?

  1. 快速消息查找 (objc_msgSend),cache_t 缓存查找消息。
  2. 慢速消息查找(lookUpImpOrForward)递归自己以及父类,自己找不到去父类缓存中找,依然找不到会进行父类慢速查找,直到找到nil。
  3. 查找不到消息进行动态方法解析(resolveInstanceMethod/resolveClassMethod)。resolveClassMethod的过程中如果没有找到方法,会调用resolveInstanceMethod
  4. 消息快速转发(forwardingTargetForSelector),相当于找消息备用接收者。
  5. 消息慢速转发(methodSignatureForSelector & forwardInvocation),在仍然没有解决问题后在methodSignatureForSelector的时候会再进行一次慢速消息查找(这次不进行消息转发)。
  6. 最后仍然没有解决问题会进入doesNotRecognizeSelector抛出异常。
  1. ⾸先明⽩我们要找到书本的什么内容 (sel⽬录⾥⾯的名称)
  2. 通过名称找到对应的本⻚码 (imp)
  3. 通过⻚码去定位具体的内容

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

不能向编译后的得到的类中增加实例变量

原因:我们编译好的实例变量存储的位置在ro,⼀旦编译完成,内存结构就完全确定了,是⽆法进行任何修改的。

只要内没有注册到内存还是可以添加

可以通过关联对象的方式添加属性,方法等
主要用到了objc_setAssociatedObjectobjc_getAssociatedObject以及objc_removeAssociatedObjects方法

当我们的对象释放的时候 --> dealloc
1: c++ 函数释放: object_cxxDestruct
2: 移除关联属性 : _object_remove_assocations
3: 将弱引⽤⾃动设置 nil : weak_clear_no_lock(&table.weak_table, (id)this)
4: 引⽤计数处理: table.refcnts.erase(this)
4: 销毁对象: free(obj)

2.3 [self class]和[super class]的区别以及原理分析。

我先来看看如下,代码

@implementation LGTeacher
- (instancetype)init{
    self = [super init];
    if (self) {
        NSLog(@"%@ - %@",[self class],[super class]);
    }
    return self;
}

代码运行结果如下:

2021-07-30 10:49:10.213169+0800 ObjcBuild[65615:2453340] LGTeacher--LGTeacher

[self class]打印结果可以理解,这[super class]打印就很懵了

what?

太意想不到了,那么clang一下看看,是objc_msgSendSuper

clang

之后debug一下,汇编跟踪看看

在这里插入图片描述

不看不要紧,一看更懵逼!什么鬼👻???不是发送objc_msgSendSuper消息吗?怎么又变成objc_msgSendSuper2了啊!现在脑壳嗡嗡的!百思不得其解。

- (Class)class {
    return object_getClass(self);
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )

objc_msgSendSuper有两个参数objc_superSEL

/// Specifies the superclass of an instance. 
struct objc_super {
    __unsafe_unretained _Nonnull id receiver;
#if !defined(__cplusplus)  &&  !__OBJC2__
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
};
#endif

其中的一个参数receiver是消息接收者,super_class为第一个被查找的类,但是实际它调用的是objc_msgSendSuper2,上面也验证过了。

// objc_msgSendSuper2() takes the current search class, not its superclass.
OBJC_EXPORT id _Nullable
objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)

从源码的注释中也可以发现super_class应该就是当前类。

    ENTRY _objc_msgSendSuper
    UNWIND _objc_msgSendSuper, NoFrame
    //p0存储receiver,p16存储class
    ldp p0, p16, [x0]       // p0 = real receiver, p16 = class
    //跳转到 L_objc_msgSendSuper2_body 的实现
    b L_objc_msgSendSuper2_body

    END_ENTRY _objc_msgSendSuper

    // no _objc_msgLookupSuper

    ENTRY _objc_msgSendSuper2
    UNWIND _objc_msgSendSuper2, NoFrame

#if __has_feature(ptrauth_calls)
    ldp x0, x17, [x0]       // x0 = real receiver, x17 = class
    //读取
    add x17, x17, #SUPERCLASS   // x17 = &class->superclass
    ldr x16, [x17]      // x16 = class->superclass
    AuthISASuper x16, x17, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS
LMsgSendSuperResume:
#else
    //ldp读取两个寄存器,将objc_super解析成receiver和class
    ldp p0, p16, [x0]       // p0 = real receiver, p16 = class
    //通过class找到superclass
    ldr p16, [x16, #SUPERCLASS] // p16 = class->superclass
#endif
L_objc_msgSendSuper2_body:
    //查找缓存
    CacheLookup NORMAL, _objc_msgSendSuper2, __objc_msgSend_uncached

    END_ENTRY _objc_msgSendSuper2

从上面arm64的汇编源码可以知道_objc_msgSendSuper跳转到_objc_msgSendSuper2,区别是_objc_msgSendSuper直接调用,objc_msgSendSuper2通过cls获取了superClass。也就是说objc_msgSendSuper传递的objc_supersuperClass为父类,objc_msgSendSuper2传递的objc_supersuperClass为自己,在汇编代码中进行了父类的获取。

那么[super class]receiver决定了消息的接收者,从上面的解释中也可以知道,这里的接受者还是self也就是LGTeacher

所以[super class]也打印的是LGTeacher

llvm
更多内容持续更新

🌹 喜欢就点个赞吧👍🌹

🌹 觉得有收获的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹

🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹

上一篇下一篇

猜你喜欢

热点阅读