底层代码
2020-08-18 本文已影响0人
Jean_Lina
9 Category的加载处理过程:
1 通过Runtime加载某个类的所有分类数据
2 把所有分类Category的方法、属性、协议合并到一个大数组中,后面参与编译的Category数据,放到数组最前面
3 将合并后的分类数据(方法、属性、协议),插入到类原来的数据前面
10 Category的实现原理
Category编译之后的底层结构是struct category_t,里面放着对象方法、类方法、协议信息、属性信息,程序在运行的时候runtime会把Category分类里面的数据合并到类信息中,类对象、元类对象中。
11 Category和Extension的区别是什么?
Extension是在编译的时候数据已经合并到类信息中。
Category是在程序运行的时候,将分类里面的数据合并到类信息中。
12 +load方法在runtime加载类、分类的时候调用
每个类、分类的+load,在程序运行的时候,只调用一次。
+load的调用顺序:
(1)先调用类的+load
按照编译先后顺序调用(先编译,先调用)
调用子类的+load之前会调用父类的+load
(2)再调用分类的+load
按照编译先后顺序调用(先编译,先调用)
13 Category中有load方法吗?load方法是什么时候调用的?load方法能继承吗?
有+load方法。
+load方法是在runtime加载类、分类的时候调用。
+load方法可以继承,一般情况下我们不手动调用,都是系统自动调用。
14 load、initialize方法的区别是什么?他们在Category中的调用顺序?以及出现继承时他们之间的调用流程?
调用方式:
+load是根据函数地址直接调用
+initialize是通过objc_msgSend进行调用。
调用时刻:
load是runtime加载类、分类的时候调用(系统直接调用一次)
initialize是类第一次接收到消息的时候调用。
调用顺序:
load先调用类的load,再调用分类的load。
先编译的类先调用,
调用子类的load之前会先调用父类的load。
先编译的分类先调用。
initialize先初始化父类的initialize,再初始化子类的initialize,每个类只会initialize初始化一次。如果子类没有实现+initialize,会调用父类的+initialize。所以父类的+initialize可能会被调用多次。
如果分类实现了+initialize,就会覆盖类本身的+initialize调用。
15 Category中能否添加成员变量?如果可以,如何给Category添加成员变量?
Category中不能直接添加成员变量
可以间接通过关联对象给分类添加成员变量
16 +initialize方法会在类第一次接收到消息时调用。
调用顺序:先调用父类的+initialize,后调用子类的+initialize。如果父类已经调用过+initialize就不再调用。
17 block的原理是什么?本质是什么?
block是封装了函数调用和函数调用环境的OC对象。
block本质也是一个OC对象,内部也有一个isa指针。
18 __block的作用是什么?有什么使用注意点?
__block可以用于解决block内部无法修改auto变量值的问题。
__block修饰变量后,编译器会把变量包装成一个对象。
使用注意点:避免对象的循环引用。
19 block的属性修饰词为什么是copy?使用block有哪些使用注意?
block如果不copy就不会在堆上。在堆上更方便管理block的生命周期。
block的使用注意:避免循环引用。
20 block内部在修改NSMutableArray,需不需要添加__block?
不需要
block有个变量捕获机制
block内部可以捕获auto局部变量,值传递。
block内部可以捕获static局部变量,指针传递。
block内部不捕获全局变量,直接访问。
block有三种类型:
__NSGlobalBlock__(block内部没有访问了auto变量)
__NSStackBlock__(block内部访问了auto变量)
__NSMallocBlock__:__NSStackBlock__的blockcopy之后是一个__NSMallocBlock__
判断block类型:
[block class];
找block父类:[[block class] superclass];
应用程序的内存分配:
程序区域 .text区 (代码段)
数据区域 .data区(全局变量) -> __NSGlobalBlock
堆 -> (alloc出来的对象,动态申请内存,程序员自己申请和管理)__NSMallocBlock
栈 -> (局部变量,系统自动分配内存和释放)__NSStackBlock
ARC环境下,编译器会根据情况,自动将栈区的block拷贝到堆区。
(1)block作为函数返回值。
(2)block赋值给__strong指针时
(3)block作为GCD的API参数
OC源码链接:[https://opensource.apple.com/tarballs/objc4/]
OC代码转换为C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
使用weak、strong引用:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m -o main.cpp
//SideTable结构体
struct SideTable {
spinlock slock;
RefcountMap refcnts; //引用计数存储,当前最新的指针为key,value为引用计数。
weak_table_t weak_table; //weak引用存储,散列表
}
struct objc_class {
Class isa;
Class superclass;
cache_t cache; //方法缓存
class_data_bits_t bits; //获取具体的类信息
};
struct method_t {
SEL name; //函数名
const char *types; //编码(返回值类型,参数类型)
MethodListIMP imp; //指向函数的指针(函数地址)
};
struct cache_t {
struct bucket_t *_buckets; //散列表
mask_t _mask; //散列表的长度减1
mask_t _occupied; //已经缓存的方法数量
};
struct bucket_t {
cache_key_t _key; // SEL作为key
IMP _imp; //函数的内存地址
};
自旋锁会忙等: 所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释放锁。
互斥锁会休眠: 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时cpu可以调度其他线程工作。直到被锁资源释放锁。此时会唤醒休眠线程。
截屏2020-08-29 上午1.28.07.png
截屏2020-08-29 上午1.18.55.png