iOS探索 runtime面试题分析(五、利用runtime-API创建对象)

五、利用runtime-API创建对象

这题对runtime-API要求程度比较高

1.API介绍

  1. 动态创建类
/**
 *创建类
 *
 *superClass: 父类,传Nil会创建一个新的根类
 *name: 类名
 *extraBytes: 额外的内存空间,一般传0
 *return:返回新类,创建失败返回Nil,如果类名已经存在,则创建失败
 */
Class FXPerson = objc_allocateClassPair([NSObject class], "LGPerson", 0);

  1. 添加成员变量
/**
*添加成员变量
*这个函数只能在objc_allocateClassPair和objc_registerClassPair之前调用。不支持向现有类添加一个实例变量
*这个类不能是元类,不支持在元类中添加一个实例变量
*实例变量的最小对齐为1 << align,实例变量的最小对齐依赖于ivar的类型和机器架构。对于任何指针类型的变量,请通过log2(sizeof(pointer_type))
*
*cls 往哪个类添加
*name 添加的名字
*size 大小
*alignment 对齐处理方式
*types 签名
*/
class_addIvar(FXPerson, "fxName", sizeof(NSString *), log2(sizeof(NSString *)), "@");

  1. 注册到内存
/**
 *往内存注册类
 *
 * cls 要注册的类
 */
 objc_registerClassPair(FXPerson);

  1. 添加属性变量
/**
*往类里面添加属性
*
*cls 要添加属性的类
*name 属性名字
*attributes 属性的属性数组。
*attriCount 属性中属性的数量。
*/
class_addProperty(targetClass, propertyName, attrs, 4);

  1. 添加方法
/**
 *往类里面添加方法
 *
 *cls 要添加方法的类
 *sel 方法编号
 *imp 函数实现指针
 *types 签名
 */
class_addMethod(FXPerson, @selector(setHobby), (IMP)fxSetter, "v@:@");

2.整体使用

// hobby的setter-IMP
void fxSetter(NSString *value) {
    printf("%s/n",__func__);
}

// hobby的getter-IMP
NSString *fxHobby() {
    return @"iOS";
}

// 添加属性变量的封装方法
void fx_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);
}

// 打印属性变量的封装方法
void fx_printerProperty(Class targetClass){
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(targetClass, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
    }
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 动态创建类
        Class FXPerson = objc_allocateClassPair([NSObject class], "FXPerson", 0);
        // 添加成员变量
        class_addIvar(FXPerson, "name", sizeof(NSString *), log2(sizeof(NSString *)), "@");
        // 注册到内存
        objc_registerClassPair(FXPerson);
        // 添加属性变量
        fx_class_addProperty(FXPerson, "hobby");
        fx_printerProperty(FXPerson);
        // 添加方法(为属性方法添加setter、getter方法)
        class_addMethod(FXPerson, @selector(setHobby:), (IMP)fxSetter, "v@:@");
        class_addMethod(FXPerson, @selector(hobby), (IMP)fxHobby, "@@:");

        // 开始使用
        id person = [FXPerson alloc];
        [person setValue:@"Felix" forKey:@"name"];
        NSLog(@"FXPerson的名字是:%@ 爱好是:%@", [person valueForKey:@"name"], [person valueForKey:@"hobby"]);
    }
    return 0;
}

3.注意事项

  • 记得导入<objc/runtime.h>
  • 添加成员变量class_addIvar必须在objc_registerClassPair前,因为注册到内存时ro已经确定了,不能再往ivars添加(同第四个面试题)
  • 添加属性变量class_addProperty可以在注册内存前后,因为是往rw中添加的
  • class_addProperty中“属性的属性”——nonatomic/copy是根据属性的类型变化而变化的
  • class_addProperty不会自动生成setter和getter方法,因此直接调用KVC会崩溃

不只可以通过KVC打印来检验,也可以下断点查看ro、rw的结构来检验

2020面试刷题与技术储备专区

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容