一、类别中添加属性
新建一个Person
类, 添加一个name
属性。
@interface Person : NSObject
@property (nonatomic) NSString * name;
@end
@implementation Person
@end
建一个Person
类的类别stature
,添加一个height
属性。
@interface Person (stature)
@property (nonatomic) NSInteger height;
@end
@implementation Person (stature)
@end
然后在调用的时候发现
image.png
setHeight:方法未找到
//获取property列表
unsigned int count = 0;
objc_property_t * list = class_copyPropertyList([p class], &count);
for (int i = 0 ; i < count; i ++) {
objc_property_t property = list[I];
const char * name = property_getName(property);
NSLog(@"property - %@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
}
free(list);
//获取ivar列表
unsigned int ivarCount = 0;
Ivar * ivars = class_copyIvarList([p class], &ivarCount);
for (int i = 0; i < ivarCount; i ++) {
Ivar ivar = ivars[I];
const char * name = ivar_getName(ivar);
NSLog(@"ivar - %@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
}
free(ivars);
//获取方法列表
unsigned int methodCount = 0;
Method * methods = class_copyMethodList([p class], &methodCount);
for (int i = 0; i < methodCount; i ++) {
Method method = methods[I];
SEL sel = method_getName(method);
NSLog(@"method - %@", NSStringFromSelector(sel));
}
free(methods);
输出
property - height
property - name
property - age
ivar - _name
ivar - _age
method - .cxx_destruct
method - name
method - setName:
method - age
method - setAge:
发现只是添加了property,并没有自动生成成员变量和set、get方法。
手动添加setter/getter方法,使用runtime关联属性后即可正常使用。
- (void)setHeight:(NSInteger)height {
const char * key = "height";
objc_setAssociatedObject(self, key, @(height), OBJC_ASSOCIATION_ASSIGN);
}
- (NSInteger)height {
const char * key = "height";
return [objc_getAssociatedObject(self, key) integerValue];
}
二、动态添加类
const char * className = "MyClass";
Class MyClass = objc_allocateClassPair([NSObject class], className, 0);
//在objc_allocateClassPair 和 objc_registerClassPair之间添加变量,不然会添加失败。
objc_registerClassPair(MyClass);
官方文档描述
三、添加实例变量
const char * key = "name";
BOOL isSuccess = class_addIvar(MyClass, key, sizeof(NSString *), 0, "@");
NSLog(@"name添加%@", isSuccess ? @"成功":@"失败");
objc_registerClassPair(MyClass);
const char * ageKey = "age";
isSuccess = class_addIvar(MyClass, ageKey, sizeof(unsigned int), 0, "I");
NSLog(@"age添加%@", isSuccess ? @"成功":@"失败");
输出
name添加成功
age添加失败
原因
1.因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定,同时runtime会调用 class_setvarlayout 或 class_setWeaklvarLayout 来处理strong weak 引用.所以不能向存在的类中添加实例变量。
2.运行时创建的类是可以添加实例变量,调用class_addIvar函数. 但是的在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
https://www.jianshu.com/p/faf14147c25d
//变量存取
id myclass = [[MyClass alloc] init];
Ivar nameIvar = class_getInstanceVariable(MyClass, key);
object_setIvar(myclass, nameIvar, @"小强");
NSLog(@"%@", object_getIvar(myclass, nameIvar));
四、动态添加属性
Property Type String
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6
const char * propertyDicKey = "_dictForCustProperty";
{
//存储添加的属性与值
class_addIvar(MyClass, propertyDicKey, sizeof([NSMutableDictionary class]), 0, "@");
id myClass = [[MyClass alloc] init];
//创建实例之后初始化一下
NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
Ivar propertyIvar = class_getInstanceVariable(MyClass, propertyDicKey);
object_setIvar(myClass, propertyIvar, dic);
}
- (void)addPropertyToClass:(Class)class propertyName:(NSString *)propertyName typeClass:(Class)typeClass {
objc_property_attribute_t type = {"T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass(typeClass)] UTF8String]};
//C copy 参考Property Type String
objc_property_attribute_t ownership = {"C", ""};
objc_property_attribute_t backingivar = {"V", "_name"};
objc_property_attribute_t attrs[] = {type, ownership, backingivar};
//添加属性
class_addProperty(class, [propertyName UTF8String], attrs, 3);
//添加 setter / getter
SEL setterSel = NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]);
class_addMethod(class, setterSel, (IMP)setValue, "@@:");
class_addMethod(class, NSSelectorFromString(propertyName), (IMP)getter, "v@:");
}
void setValue (id self, SEL _cmd, id value) {
//sel转为key setName: --> name
NSString * key = [NSStringFromSelector(_cmd) stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""];
NSString * head = [[key substringWithRange:NSMakeRange(0, 1)] lowercaseString];
key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:head];
key = [key substringToIndex:key.length - 1];
Ivar ivar = class_getInstanceVariable([self class], propertyDicKey);
NSMutableDictionary * dic = object_getIvar(self, ivar);
[dic setObject:value forKey:key];
object_setIvar(self, ivar, dic);
}
id getter (id self, SEL _cmd) {
Ivar ivar = class_getInstanceVariable([self class], propertyDicKey);
NSMutableDictionary * dic = object_getIvar(self, ivar);
return [dic objectForKey:NSStringFromSelector(_cmd)];
}
五、动态添加方法
{
//添加实例方法
class_addMethod(MyClass, @selector(methodTest), (IMP)methodTestIMP, "v@:");
objc_msgSend(myclass, @selector(methodTest));
//添加类方法
class_addMethod(object_getClass(MyClass), @selector(classMethodTest), (IMP)classMethodTestIMP, "v@:");
objc_msgSend(MyClass, @selector(classMethodTest));
}
void methodTestIMP (id self, SEL _cmd) {
NSLog(@"这是一个实例方法");
};
void classMethodTestIMP (id self, SEL _cmd) {
NSLog(@"这是一个类方法");
};
六、方法交换
+ (void)load {
Method old = class_getInstanceMethod([self class], @selector(viewWillAppear:));
Method new = class_getInstanceMethod([self class], @selector(track_viewWillAppear:));
method_exchangeImplementations(old, new);
}
- (void)track_viewWillAppear:(BOOL)animated {
NSLog(@"%@---%@",self , NSStringFromSelector(_cmd));
[self track_viewWillAppear:animated];
}
网友评论