OC 反射-->动态创建类
系统方法
NSLog(@"%s", __func__); //打印出类的方法名称,如:
//打印结果:2018-02-22 10:52:15.394575+0800 DemoRuntime[1078:25503] -[ViewController printIvarList]
创建Class
1 - (Class) createNewClass { 2 const char * className; 3 className = [@"Student" UTF8String]; 4 Class kclass = objc_getClass(className); 5 //判断此类是否已经存在,如果存在则返回,不存在就创建 6 if (!kclass) 7 { 8 Class superClass = [NSObject class]; 9 kclass = objc_allocateClassPair(superClass, className, 0); 10 } 11 return kclass; 12 }
注册类
1 Class newClass = [self createNewClass]; 2 id instanceObjects = [[newClass alloc] init]; 3 NSLog(@"注册后的类实例化对象:%@", instanceObjects);
打印结果:
添加成员变量
1 //添加成员变量 2 - (void) addNewVariable:(Class) newClass { 3 class_addIvar(newClass, [@"_stuName" UTF8String], sizeof(NSString *), log2(sizeof(id)), "@");
4 }
1 //动态添加变量 2 //入参:类Class,变量名char数组,变量类型大小size_t,变量在内存中的对齐方式,变量的type类型 3 //返回:添加结果,是否成功。 4 //* 1.只能给动态创建的类添加变量也就是用 objc_allocateClassPair 创建的类 5 //* 2.添加变量只能在函数 objc_allocateClassPair 和 class_getInstanceVariable 之间添加才有效 6 BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types)
注意:这个方法的调用要在objc_allocateClassPair方法之后和注册类objc_registerClassPair方法之前调用,否则没法动态添加成员变量。
这是因为方法和属性并不“属于”类实例,而成员变量“属于”类实例。我们所说的“类实例”概念,指的是一块内存区域,包含了
isa
指针和所有的成员变量。所以假如允许动态修改类成员变量布局,已经创建出的类实例就不符合类定义了,变成了无效对象。但方法定义是在objc_class
中管理的,不管如何增删类方法,都不影响类实例的内存布局,已经创建出的类实例仍然可正常使用。
获取Class的成员变量名
1 // 获取类的成员变量名 2 - (NSArray *)getVariableNamesByObject:(id)object 3 { 4 unsigned int numIvars = 0; 5 // 获取类的所有成员变量 6 Ivar * ivars = class_copyIvarList([object class], &numIvars); 7 // 定义一个数组来接收获取的属性名 8 NSMutableArray *nameArray = [[NSMutableArray alloc] initWithCapacity:numIvars]; 9 for(int i = 0; i < numIvars; i++) { 10 // 得到单个的成员变量 11 Ivar thisIvar = ivars[i]; 12 // 得到这个成员变量的类型 13 const char *type = ivar_getTypeEncoding(thisIvar); 14 NSString *stringType = [NSString stringWithCString:type encoding:NSUTF8StringEncoding]; 15 // 此处判断非object-c类型时跳过 16 if (![stringType hasPrefix:@"@"]) { 17 continue; 18 } 19 // 得到成员变量名 20 NSString *variableName = [NSString stringWithUTF8String:ivar_getName(thisIvar)]; 21 [nameArray addObject:variableName]; 22 23 // 这个函数可以得到成员变量的值 24 // object_getIvar(object, thisIvar) 25 26 } 27 free(ivars); 28 return nameArray; 29 }
调用以后,结果为:
创建方法
第一个参数为类名,第二个参数为方法名,第三个参数是函数名,第四个参数是函数的返回值和参数的类型,v表是void,@表示id,:表示SEL。更多多定义参考:SELECTOR
1 class_addMethod([kclass class], @selector(say:), (IMP)say, "v@:");
1 //动态添加方法 2 //入参:类Class,方法名SEL,方法实现IMP,方法返回值各个参数类型等配置字符串 3 //返回:添加结果,是否成功。 4 //* 1.添加属性不用再objc_registerClassPair之前,因为添加属性其实就是添加变量的set 和 get方法而已 5 //* 2.添加的属性和变量不能用kvc设置值和取值 6 BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
需要实现,这个方法
添加属性
1 //添加一个属性 2 - (void) addProperties:(Class) newClass { 3 NSString *propertyName = @"stuSex"; 4 objc_property_attribute_t type = { "T", "@\"NSString\"" }; 5 objc_property_attribute_t ownership = { "C", "copy" }; 6 objc_property_attribute_t backingivar = { "V", [propertyName UTF8String]}; 7 objc_property_attribute_t attrs[] = { type, ownership, backingivar }; 8 BOOL isOk=class_addProperty(newClass, [propertyName UTF8String], attrs, 3); 9 }
1 //动态添加属性 2 //入参:类Class,属性名char数组,属性的配置属性,objc_property_attribute_t,属性的属性数量。 3 //返回:添加结果,是否成功。 4 BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
或者:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 + (void)addPropertyWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value { 2 3 //先判断有没有这个属性,没有就添加,有就直接赋值 4 Ivar ivar = class_getInstanceVariable([target class], [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]); 5 if (!ivar) { 6 return; 7 } 8 //objc_property_attribute_t所代表的意思可以调用getPropertyNameList打印,大概就能猜出 9 objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([value class])] UTF8String] }; 10 objc_property_attribute_t ownership = { "&", "N" }; 11 objc_property_attribute_t backingivar = { "V", [[NSString stringWithFormat:@"_%@", propertyName] UTF8String] }; 12 objc_property_attribute_t attrs[] = { type, ownership, backingivar }; 13 if (class_addProperty([target class], [propertyName UTF8String], attrs, 3)) { 14 15 //添加get和set方法 16 SEL getter = NSSelectorFromString(propertyName); 17 SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]); 18 19 BOOL suc0 = class_addMethod([target class], getter, (IMP)attribute0Getter, "@@:"); 20 BOOL suc1 = class_addMethod([target class], setter, (IMP)attribute0Setter, "v@:@"); 21 NSLog(@">>>>>>>>添加get和set成功OrFail:%@:%@",@(suc0),@(suc1)); 22 23 NSLog(@">>>>>>>>1:%@",[target performSelector:getter withObject:nil]); 24 [target performSelector:setter withObject:@"为动态创建类先添加变量再添加属性"]; 25 NSLog(@">>>>>>>>2:%@",[target performSelector:getter withObject:nil]); 26 27 //赋值 28 [target setValue:value forKey:propertyName]; 29 NSLog(@"%@", [target valueForKey:propertyName]); 30 31 NSLog(@"创建属性Property成功"); 32 } 33 }
1 //get方法 2 NSString *attribute0Getter(id classInstance, SEL _cmd) { 3 Ivar ivar = class_getInstanceVariable([classInstance class], "_attribute0");//获取变量,如果没获取到说明不存在 4 return object_getIvar(classInstance, ivar); 5 } 6 7 //set方法 8 void attribute0Setter(id classInstance, SEL _cmd, NSString *newName) { 9 Ivar ivar = class_getInstanceVariable([classInstance class], "_attribute0");//获取变量,如果没获取到说明不存在 10 id oldName = object_getIvar(classInstance, ivar); 11 if (oldName != newName) object_setIvar(classInstance, ivar, [newName copy]); 12 }
get方法:第一个个@代表返回的类型为非基本数据类型,如果返回的数据是int那么第一个字符应该为i set方法:第一个个v代表返回的类型为void,如果返回的数据是int那么第一个字符应该为i,最后一个@代表函数的第一个试用参数类型为非基本数据类型 set和get方法的共同部分是@:分别代表方法的两个默认函数target和SEL。
打印结果:
查看属性
1 // 获取类的所有属性名 2 - (NSArray*)getPropertieNamesByObject:(id)object 3 { 4 5 unsigned int outCount, i; 6 7 // 获取注册类的属性列表,第一个参数是类,第二个参数是接收类属性数目的变量 8 objc_property_t *properties = class_copyPropertyList([object class], &outCount); 9 // 定义一个数组来接收获取的属性名 10 NSMutableArray *nameArray = [[NSMutableArray alloc] initWithCapacity:outCount]; 11 for (i = 0; i < outCount; i++) { 12 // 通过循环来获取单个属性 13 objc_property_t property = properties[i]; 14 // 取得属性名 15 NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; 16 // 将得到的属性名放入数组中 17 [nameArray addObject:propertyName]; 18 19 } 20 free(properties); 21 return nameArray; 22 }
打印结果:
查看方法
1 -(void)getMethodsListByObject:(id) object { 2 unsigned int copycopyMethodListCount = 0; 3 Method *methods = class_copyMethodList([object class], ©copyMethodListCount); 4 for (NSInteger i = 0; i < copycopyMethodListCount; i++) { 5 Method method = methods[i]; 6 SEL name = method_getName(method); 7 NSLog(@">>>>>>>>2:copyMethodList:%@",NSStringFromSelector(name)); 8 } 9 free(methods);//释放 10 NSLog(@"\n"); 11 12 }
打印:
添加协议
1 //添加协议 2 - (void)addProtocoalWithClass:(Class)class_1 { 3 BOOL result0 = class_addProtocol(class_1, NSProtocolFromString(@"UITableViewDelegate")); 4 NSLog(@">>>>>>>>3:添加协议成功"); 5 6 /** 7 * 1.class_addProtocol 参数含义:第一个:要添加协议的类,第二个:协议对象 8 * 2.获取协议列表具体细节参照Class1里的内容 9 */ 10 unsigned int copyProtocolListCount = 0; 11 Protocol * __unsafe_unretained *protocals = class_copyProtocolList(class_1, ©ProtocolListCount); 12 for (NSInteger i = 0; i < copyProtocolListCount; i++) { 13 Protocol * protocal = protocals[i]; 14 const char *name = protocol_getName(protocal); 15 NSLog(@">>>>>>>>4:copyProtocolList:%s",name); 16 } 17 free(protocals);//释放 18 NSLog(@"\n"); 19 }
打印结果: