runtime 方法交换
#import <objc/runtime.h>
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class aClass = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
// When swizzling a class method, use the following:
// Class aClass = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(aClass, originalSelector);
// Method swizzledMethod = class_getClassMethod(aClass, swizzledSelector);
// 往类中添加方法
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
//如果添加成功,就带类中不存在要替换的方法
if (didAddMethod) {
class_replaceMethod(aClass,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
//反之,类中已经有了想要替换的方法时
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
@end
1. 动态添加Ivar
优点:
- 动态添加Ivar我们能够通过遍历Ivar得到我们所添加的属性。
缺点:
- 不能在已存在的class中添加Ivar,所以说必须通过objc_allocateClassPair动态创建一个class,才能调用class_addIvar创建Ivar,最后通过objc_registerClassPair注册class。
//在目标target上添加属性(已经存在的类不支持,可跳进去看注释),属性名propertyname,值value
- (void)addIvarWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {
if (class_addIvar([target class], [propertyName UTF8String], sizeof(id), log2(sizeof(id)), "@"))
{
NSLog(@"创建属性Ivar成功");
}
}
//获取目标target的指定属性值
- (id)getIvarValueWithTarget:(id)target withPropertyName:(NSString *)propertyName
{ Ivar ivar = class_getInstanceVariable([target class], [propertyName UTF8String]);
if (ivar) {
id value = object_getIvar(target, ivar);
return value;
} else
{
return nil;
}
}
2. 动态添加property
主要用到class_addProperty,class_addMethod,class_replaceProperty,class_getInstanceVariable
//在目标target上添加属性,属性名propertyname,值value
+ (void)addPropertyWithtarget:(id)target withPropertyName:(NSString *)propertyName withValue:(id)value {
//先判断有没有这个属性,没有就添加,有就直接赋值
Ivar ivar = class_getInstanceVariable([target class], [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]);
if (ivar)
{
return;
}
/*
objc_property_attribute_t type = { "T", "@/"NSString/"" };
objc_property_attribute_t ownership = { "C", "" }; // C = copy
objc_property_attribute_t backingivar = { "V", "_privateName" };
objc_property_attribute_t attrs[] = { type, ownership, backingivar };
class_addProperty([SomeClass class], "name", attrs, 3);
*/
//objc_property_attribute_t所代表的意思可以调用getPropertyNameList打印,大概就能猜出。
objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@/%@/",NSStringFromClass([value class])] UTF8String] };
objc_property_attribute_t ownership = { "&", "N" };
objc_property_attribute_t backingivar = { "V", [[NSString stringWithFormat:@"_%@", propertyName] UTF8String] };
objc_property_attribute_t attrs[] = { type, ownership, backingivar };
if (class_addProperty([target class], [propertyName UTF8String], attrs, 3))
{
//添加get和set方法
class_addMethod([target class], NSSelectorFromString(propertyName), (IMP)getter, "@@:");
class_addMethod([target class], NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)setter, "v@:@");
//赋值
[target setValue:value forKey:propertyName];
NSLog(@"%@", [target valueForKey:propertyName]);
NSLog(@"创建属性Property成功");
}
else {
class_replaceProperty([target class], [propertyName UTF8String], attrs, 3);
//添加get和set方法
class_addMethod([target class], NSSelectorFromString(propertyName), (IMP)getter, "@@:");
class_addMethod([target class], NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]), (IMP)setter, "v@:@");
//赋值
[target setValue:value forKey:propertyName];
}
}
id getter(id self1, SEL _cmd1)
{
NSString *key = NSStringFromSelector(_cmd1);
Ivar ivar = class_getInstanceVariable([self1 class], "_dictCustomerProperty");
//basicsViewController里面有个_dictCustomerProperty属性
NSMutableDictionary *dictCustomerProperty = object_getIvar(self1, ivar);
return [dictCustomerProperty objectForKey:key];
}
void setter(id self1, SEL _cmd1, id newValue)
{
//移除set
NSString *key = [NSStringFromSelector(_cmd1) stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""];
//首字母小写
NSString *head = [key substringWithRange:NSMakeRange(0, 1)];
head = [head lowercaseString];
key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:head];
//移除后缀 ":"
key = [key stringByReplacingCharactersInRange:NSMakeRange(key.length - 1, 1) withString:@""];
Ivar ivar = class_getInstanceVariable([self1 class], "_dictCustomerProperty");
//basicsViewController里面有个_dictCustomerProperty属性
NSMutableDictionary *dictCustomerProperty = object_getIvar(self1, ivar);
if (!dictCustomerProperty)
{
dictCustomerProperty = [NSMutableDictionary dictionary];
object_setIvar(self1, ivar, dictCustomerProperty);
}
[dictCustomerProperty setObject:newValue forKey:key];
}
+ (id)getPropertyValueWithTarget:(id)target withPropertyName:(NSString *)propertyName
{
//先判断有没有这个属性,没有就添加,有就直接赋值
Ivar ivar = class_getInstanceVariable([target class], [[NSString stringWithFormat:@"_%@", propertyName] UTF8String]);
if (ivar)
{
return object_getIvar(target, ivar);
}
ivar = class_getInstanceVariable([target class], "_dictCustomerProperty");
//basicsViewController里面有个_dictCustomerProperty属性
NSMutableDictionary *dict = object_getIvar(target, ivar);
if (dict && [dict objectForKey:propertyName]) {
return [dict objectForKey:propertyName];
}
else
{
return nil;
}
}
3. 动态添加方法
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
4. 分类添加属性
// 定义关联的key
static const char *key = "name";
@implementation NSObject (Property)
- (NSString *)name
{
// 根据关联的key,获取关联的值。
return objc_getAssociatedObject(self, key);
}
- (void)setName:(NSString *)name
{
// 第一个参数:给哪个对象添加关联
// 第二个参数:关联的key,通过这个key获取
// 第三个参数:关联的value
// 第四个参数:关联的策略
objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
5. 利用 runtime 获取所有属性来重写归档解档方法
// 设置不需要归解档的属性
- (NSArray *)ignoredNames {
return @[@"_aaa",@"_bbb",@"_ccc"];
}
// 解档方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
// 获取所有成员变量
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
// 将每个成员变量名转换为NSString对象类型
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 忽略不需要解档的属性
if ([[self ignoredNames] containsObject:key]) {
continue;
}
// 根据变量名解档取值,无论是什么类型
id value = [aDecoder decodeObjectForKey:key];
// 取出的值再设置给属性
[self setValue:value forKey:key];
// 这两步就相当于以前的 self.age = [aDecoder decodeObjectForKey:@"_age"];
}
free(ivars);
}
return self;
}
// 归档调用方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
// 获取所有成员变量
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
// 将每个成员变量名转换为NSString对象类型
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 忽略不需要归档的属性
if ([[self ignoredNames] containsObject:key]) {
continue;
}
// 通过成员变量名,取出成员变量的值
id value = [self valueForKeyPath:key];
// 再将值归档
[aCoder encodeObject:value forKey:key];
// 这两步就相当于 [aCoder encodeObject:@(self.age) forKey:@"_age"];
}
free(ivars);
}