一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。
获取所有的私有属性和方法
这个在判断是否子类重写了父类的方法时会用到。
#pragma mark - 获取所有的属性(包括私有的)
- (void)getAllIvar {
unsigned int count = 0;
Ivar *ivars= class_copyIvarList([UIPageControl class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
NSLog(@"属性 --> %@ 和 %@",name,type);
}
}
#pragma mark - 获取所有的方法(包括私有的)
- (void)getAllMethod {
unsigned int count = 0;
Method *memberFuncs = class_copyMethodList([UIPageControl class], &count);
for (int i = 0; i < count; i++) {
SEL address = method_getName(memberFuncs[i]);
NSString *methodName = [NSString stringWithCString:sel_getName(address) encoding:NSUTF8StringEncoding];
NSLog(@"方法 : %@",methodName);
}
}
对私有属性修改
有时候一般的api不能满足我们的需求,比如有的api的某些方法没有暴露出来或者部分字段没有公开,这时候我们又需要访问这些私有变量,那么这时候runtime就派上了用场。
下面是一个具体的对私有变量更改的例子。
- (void)changePrivate {
Person *onePerson = [[Person alloc] init];
NSLog(@"Person属性 == %@",[onePerson description]);
unsigned int count = 0;
Ivar *members = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++){
Ivar var = members[i];
const char *memberAddress = ivar_getName(var);
const char *memberType = ivar_getTypeEncoding(var);
NSLog(@"获取所有属性 = %s ; type = %s",memberAddress,memberType);
}
Ivar m_address = members[1];
object_setIvar(onePerson, m_address, @"上海");
NSLog(@"对私有变量的(地址)进行更改 : %@",[onePerson description]);
}
归档:解档
快速定义归档和解档属性
@implementation MMModel
- (void)encodeWithCoder:(NSCoder *)encoder {
unsigned int count = 0;
// 利用runtime获取实例变量的列表
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *nameStr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
id value = [self valueForKey:nameStr];
[encoder encodeObject:value forKey:nameStr];
}
free(ivars);
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
//
NSString *key = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
id value = [decoder decodeObjectForKey:key];
// 设置到成员变量身上
[self setValue:value forKey:key];
}
free(ivars);
}
return self;
}
7、动态的添加方法
有的时候我们的代码不是写死的,有些高级场景,比如插件啊等什么的需要我们动态生成方法动态添加,这时候我们就可以利用runtime来达到这个效果。
理解了消息转发的整个流程,就能够理解为什么这样行得通。
// 默认方法都有两个隐式参数,
void eat(id self,SEL sel){
NSLog(@"%@ %@",self,NSStringFromSelector(sel));
NSLog(@"动态添加了一个方法");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == NSSelectorFromString(@"eat")) {
class_addMethod(self, sel, (IMP)eat, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}