美文网首页
15、OC底层练习

15、OC底层练习

作者: ChenL | 来源:发表于2020-10-27 11:21 被阅读0次
一、runtime Associatie方法关联的对象,需要在dealloc 中释放吗?

当我们的对象释放时,会调用dealloc

1、C++函数释放: objc_cxxDestruct
2、移除关联属性: _objc_remove_associations
3、将弱引用自动设置nil:weak_clear_no_lock(&table.weak_table,(id)this);
4、引用计数处理:table.refcnts.erose(this)
5、销毁对象:free(obj)

所以:关联对象 不需要我们手动移除,会在对象析构 即dealloc时释放

dealloc源码分析:

dealloc 的源码查找路径为:
dealloc -> _objc_rootDealloc -> rootDealloc -> object_dispose(释放对象) ->objc_destructInstance -> _objcet_remove_associations

二、方法的调度顺序

1、类方法 和 分类方法 重名,如果调用,是什么情况?

a、如果同名方法是普通方法,包括 initialize -- 先调用分类方法

解析:

因为分类的方法是在类realize之后 attach进去的,插在类的方法的前面,所以优先调用分类的方法(注意:只是优先调用,不会覆盖主类)

initialize 方法什么时候调用? initialize 方法也是主动调用,即第一次消息时 调用,为了不影响整个load,可以将需要 提前加载的数据 写到 initialize

b、如果同名方法是load方法-- 先 主类load,后 分类 load(分类之间看编译的顺序)`

imag.png
三、 runtime 是什么?

runtime 是由 C 和C++ 汇编实现的一套API,为OC语言加入了 面向对象已经运行时的功能

运行时 是指 将 数据类型的确定 由编译时 推迟到了 运行时,例如: extension 和 category 的区别

平时编写的OC代码,在程序运行的过程中,其实最终会转换成 runtime 的C语言代码,runtime 就是 OC 的幕后工作者

Category 分类:

专门用来给类添加方法,不能给类添加成员属性(其实可以通过runtime 给分类添加属性,即属性关系,重写setter/getter方法),分类中用 @property 定义变量,只会生成变量的setter/getter方法的声明,不能生成方法实现 和 带下划线的成员变量

Extension类扩展:

可以说成是 特殊的分类,也可称为 匿名分类,可以给类 添加成员属性、添加方法(但都是私有的)

四、方法的本质,SEL 是什么? IMP是什么?两者之间的关系又是什么?

方法的本质:消息发送,消息会有以下几个流程:

1、快速查找(objc_msgSend)-cache_t缓存消息中查找
2、慢速查找- 递归自己/父类 -lookUpImpOrforward
3、动态决议 - 动态方法补救 resolveInstanceMethod
4、消息快速转发 - forwardingTargetForSelector
5、 消息慢速转发 -methodSignatureForSelector & forwardInvocation

sel 是方法方法编号, 在read_images 期间就编译进了 内存
imp 是 函数实现指针,找imp 就是 找函数的过程

sel 相当于 一本书的 目录title ,sel 相当于 书本的页码

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

1、 不能想编译后得到的类中增加实例变量,向运行时创建的类中添加实例变量
2、只要类没有注册到内存还是可以添加的
3、可以添加属性+方法

原因:编译好的实例变量存储的位置是or,一旦编译完成,内存结构就完全确定了。

六 、【self class】 与 【super class】的区别以及原理分析
  • [self class] 就是发送消息 objc_msgSend ,消息接收者是:self,方法编号class

  • [super class] 本质就是 objc_msgSendSuper, 消息的接收者 还是 self,方法编号 class, 在运行时,底层调用的是:**_objc_msgSendSuper2 **

  • 只是objc_msgSendSuper2 会更快,直接跳过 self的查找

七、Runtime 是如何实现weak的,为什么可以自动设置为nil

1、通过sideTable 找到我们的weak_table
2、weak_table 根据 referent 找到 或者 创建 weak_entry_t
3、然后appent_referrer(entry,referrer) 将我的新弱引用的对象加进去entry
4、最后 weak_entry_insert,把 entry 加入到我们的weak_table

image.png
八 、 内存平移问题
Class cls = [LGPerson class];
void  *kc = &cls;  //
[(__bridge id)kc saySomething];

LGPerson 中有一个属性kc_name 和一个 实例方法 saySomething,通过上面代码这种方式,能否调用实例方法?为什么?

代码调试:

我们在日常开发中的调用方式是下面这种:

LGPerson *person = [LGPerson alloc];
[person saySomething];

通过运行发现,是可以执行的,如下:


image.png

[person saySomething]的本质是 对象发送消息,那么当前的person 是什么?

person 的isa 指向类 LGPerson 即 person 的首地址 指向 LGPerson的首地址 ,我们 可以通过 LGPerson 的内存平移找到cache ,在cache中查找方法

在 [(__bridge id)kc saySomething]中的kc 是来自于LGPerson 这个类,然后有一个指针kc,将其 指向LGPerson的首地址

image.png

所以,person 是指向LGPerson类的结构,kc也是指向LGPerson类的结构,然后都是在LGPerson 中的methodList 中查找方法

image.png

修改: saySomething里面有属性 self.kc_name 的打印
代码如下:

- (void)saySomething{
    NSLog(@"%s - %@",__func__,self.kc_name);
}

//下面这两种方式调用
//方式一
Class cls = [LGPerson class];
void  *kc = &cls; 
[(__bridge id)kc saySomething]; 
 
//方式二:常规调用
LGPerson *person = [LGPerson alloc];
 [person saySomething];

打印结果:

image.png

kc方式的调用打印的kc_name 是 <ViewController: 0x102d0aa30>
person 方式的调用打印的 kc_name 是 (null)

why 出现的打印不一致???

  • 其中person 方式的kc_name 是由于 self指向person的内存结构,然后通过 内存平移8字节,取出 kc_name , 即 self指针首地址平移8字节获得

  • kc方式: 其中kc指针中没有任何,所以kc表示8个字节指针,self.kc_name的获取,相当于kc 首地址的指针也需要平移8字节找到kc_name , 那么此时的kc指针地址是多大??平移8 个字节获取的是什么?

kc 是 一个指针,是存在栈中的,栈是一个先进后出的结构,参数传入 就是一个不断压栈的过程。

  • 其中 隐藏参数会压入栈,且每个函数都会有两个隐藏参数(id self,sel _cmd),可以通过clang查看底层编译
  • 隐藏参数压栈 的过程,其地址是 递减的,而 栈是从高地址->低地址 分配的,即 在栈中,参数会从前往后一直压
  • super 通过clang 查看底层的编译,是 objc_msgSendSuper,其第一次参数是一个结构体__rw_objc_super(self,class_getSuperclass),那么结构体中的属性是 任何压栈的? 可以通过自定义一个结构体,判断结构体内部成员的压栈情况
image.png

图中可以得出 20 先加入 10 后加入,因此 结构体内部 的压栈情况是 低地址-> 高地址,递增的,栈中结构体内部 的成员是反向压入栈,即低地址-> 高地址,是递增的。

  • 所以到目前为止,栈中 从高地址到低地址 的顺序的:
    self - _cmd - (id)class_getSuperclass(objc_getClass("ViewController")) - self - cls - kc - person

sel 和 _cmd 是 viewDidload方法的两个隐藏参数,是 高地址-> 低地址 (正向压栈的)

class_getSuperClass 和 self 为 objc_msgsendSuper2 中的结构体成员,是从最后一个成员变量,即低地址->高地址 (方向压栈的)

可以通过下面的代码打印栈的存储是否如上面所说:

void *sp  = (void *)&self;
void *end = (void *)&person;
long count = (sp - end) / 0x8;
    
for (long i = 0; i<count; i++) {
    void *address = sp - 0x8 * i;
    if ( i == 1) {
        NSLog(@"%p : %s",address, *(char **)address);
    }else{
        NSLog(@"%p : %@",address, *(void **)address);
    }
}
image.png

其中为什么class_getSuperclass 是 ViewController,因为objc_msgSendSuper2返回的是当前类,两个self,并不是同一个self,而是栈的指针不同,但是指向同一片内存空间

  • [(__bridge id)kc saySomething]调用时,此时的kc是 LGPerson: 0x7ffeec381098,所以saySomething方法中传入的self 还是LGPerson,但并不是我们通常认为的LGPerson,使我们当前传入的消息接收者,即LGPerson: 0x7ffeec381098,是LGPerson的实例对象,此时的操作与普通的LGPerson是一致的,即LGPerson的地址内存平移8字节

普通person 流程: person -> kc_name - 内存平移8字节

kc流程:0x7ffeec381098 + 0x80 -> 0x7ffeec3810a0,即为self,指向<viewController:0x7fac45514f50>

其中 person 与LGPerson 的关系是 person 是以LGPerson为模版的实例化对象,即alloc 有一个指针地址,指向isa,isa指向LGPerson,它们之间关联是有一个isa 指向,
而kc也是指向LGPerson的关系,编译器会认为 kc也是LGPerson的一个实例化对象,即kc相当于 isa,即首地址,指向LGPerson,具有和person一样的效果,简单来说,我们已经完全将编译器骗过了,即kc也有kc_name. 由于person查找kc_name是 通过内存平移8字节,所以kc也是通过内存平移8字节找到kc_name

注:

堆 是 从小到大,即低地址-> 高地址

栈是从大到小 ,即 从高地址->低地址

函数隐藏参数会 从前往后 一直压,即高地址->低地址 开始入栈
结构体内部的员工 是 从低地址->高地址

一般情况下,内存地址有如下规则:

0x60 开头表示在
0x70 开头的地址表示在
0x10 开头的地址表示在全局区域

相关文章

  • 15、OC底层练习

    一、runtime Associatie方法关联的对象,需要在dealloc 中释放吗? 当我们的对象释放时,会调...

  • iOS--OC底层原理文章汇总

    OC底层原理01—alloc + init + new原理OC底层原理02—内存对齐OC底层原理03— isa探究...

  • OC底层原理汇总

    OC底层原理(一).alloc实际调用流程分析OC底层原理(二).内存分配与内存对齐OC底层原理(三)、isa、对...

  • OC底层原理16-objc_init初探 & dyld和objc

    引入在 OC底层原理15-dyld加载流程[https://www.jianshu.com/p/3a238256a...

  • 2021 iOS底层提升计划

    iOS底层提升方案 下方学习大纲大家可以参考学习《OC底层、核心编程探索》专栏的索引。 OC底层探索 OC对象占用...

  • iOS - 对象内存分布

    [toc] 参考 对象内存分布 intro OC底层 我们平时编写的OC代码,底层实现其实都是C\C++代码 OC...

  • OC对象的本质

    一、OC对象的底层实现 OC 中的代码在底层实现,使用的是 C、C++,所以要研究 OC 中的类结构,可以将 OC...

  • OC底层原理探索文档汇总

    OC底层探索: 01-OC对象的底层分析[https://www.jianshu.com/p/953fcfddde...

  • OC对象底层的探索

    OC对象的底层分析 OC底层原理探索文档汇总[https://www.jianshu.com/p/04883ea4...

  • iOS底层 -- OC对象底层本质

    NSObject的底层实现 Student的底层实现 OC对象继承关系的底层实现

网友评论

      本文标题:15、OC底层练习

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