前言
类的动态加载
类的动态加载实际上就是运用runtime
运行时来加载一个类。
动态创建例子
下面贴上一个动态创建的方式
一个类的动态创建、为类增加成员变量就是这样的一个流程,下面对每段API
进行分析
这里注意一下 如果第2步和第3步调换一下位置,并且还调用`setValue:forKey:`方法会出现奔溃,将会在下面分析。
动态创建API
分析
1. objc_allocateClassPair
创建类
objc_allocateClassPair(<#Class _Nullable __unsafe_unretained superclass#>, <#const char * _Nonnull name#>, <#size_t extraBytes#>)
- 参数1
superclass
创建类的父类。 - 参数2
name
创建类的名字。 - 参数3
extraBytes
提供类的内存字节,可以为0
2. class_addIvar
为类增加成员变量
class_addIvar(<#Class _Nullable __unsafe_unretained cls#>, <#const char * _Nonnull name#>, <#size_t size#>, <#uint8_t alignment#>, <#const char * _Nullable types#>)
- 参数1
cls
动态创建的类。 - 参数2
name
成员变量的名字。 - 参数3
size
该成员变量的字节大小,可以直接用sizeof(NSString *)
来获取。 - 参数5
types
该成员变量的类型。 - 参数4
alignment
这个参数我们不知道怎么填,可以深入API
里面去查看看。 我们知道NSString
的字节大小为8,通过log2()
函数可以获取到该值,并且对应到API
里面1<<alignment
即为我们获取到的值。
3. objc_registerClassPair()
注册类到内存
动态创建顺序
class_addIvar()
函数里面
rw
的flags
标志了已经构造 就直接返回NO
。
objc_registerClassPair
综合上面的两个函数分析,可以知道如果类注册之后就更改相应的构造标志信息,如果再去增加成员变量是无法增加的。
动态创建增加属性
1. 增加属性
Class cls = objc_allocateClassPair([NSObject class], "XDStudent", 0);
objc_registerClassPair(cls);
xd_class_addProperty(cls, "name");
id student = [cls alloc];
[student setValue:@"123HH" forKey:@"name"];
NSLog(@"%@",[student valueForKey:@"name"]);
void xd_class_addProperty(Class targetClass , const char *propertyName){
objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([NSString class])] UTF8String] }; //type
objc_property_attribute_t ownership0 = { "C", "" }; // C = copy
objc_property_attribute_t ownership = { "N", "" }; //N = nonatomic
objc_property_attribute_t backingivar = { "V", [NSString stringWithFormat:@"_%@",[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]].UTF8String }; //variable name
objc_property_attribute_t attrs[] = {type, ownership0, ownership,backingivar};
class_addProperty(targetClass, propertyName, attrs, 4);
}
这里面是封装的一个增加属性函数,主要是objc_property_attribute_t
会比较复杂一点。
@property (nonatomic, copy) NSString *nickName;
我们可以看上面的这段代码,
nickName
是某个对象的属性,但是属性也有自己的属性,比如nonatomic
、copy
、NSString
。- 在上面处理
objc_property_attribute_t
的时候,有固定的写法,type
,backingivar
这两个变量。有通过属性的属性来对应不同的写法,比如ownership0
,ownership
这两个变量。
运行分析
增加属性之后,直接`setValue:forKey:`运行之后会出现奔溃的情况,报错的原因是没有找到`set`方法。
我们知道属性在编译阶段会自动生成成员变量,setter,getter方法,但是这里我们只是动态的增加了属性,并没有setter方法,所有会报错。接下来增加方法
2. 增加方法
Class cls = objc_allocateClassPair([NSObject class], "XDStudent", 0);
objc_registerClassPair(cls);
xd_class_addProperty(cls, "name");
class_addMethod(cls, @selector(setName:), (IMP)xd_setName, "v@:@");
class_addMethod(cls, @selector(name), (IMP)xd_name, "@@:");
id student = [cls alloc];
[student setValue:@"123HH" forKey:@"name"];
NSLog(@"%@",[student valueForKey:@"name"]);
为动态类增加setter
、getter
方法,并且给定相应的IMP
。
动态加载总结
- 这里就是涉及到了ro与rw的关系。
- 成员变量在ro结构体里面,命名为只读类型,则需要在类注册到内存之前就完成相应的赋值操作。
- 属性、方法在类注册到内存之后,还有一个赋值在rw里面,命名为读写类型。
类和分类的加载
不管是类还是分类都区分懒加载和非懒加载,主要的区别就是有没有实现load
方法,接下来我们开始组合分析。
懒加载类 + 懒加载分类
懒加载类 + 非懒加载分类
非懒加载类 + 懒加载分类
非懒加载类 + 非懒加载分类
学习之路,砥砺前行
不足之处可以在评论区指出