1.isa指针
Person * person = [[Person alloc]init];
[person getName];
一个Person对象person调用getName的方法,getName方法是在类中声明的,也就是对象调用的方法实际是去类中查找的,如果有接直接调用,如果该类没有此方法,那么就去父类中查找该方法,一直没有直到NSObject,再没有就报错了。那么如何能从对象找到对应的类呢,这里就引入了isa指针。
每一个对象都有一个isa指针,指向该对象的类。
其实类本身也是一个对象,它的isa指针指向元类。
元类也是一个对象,它的isa指针指向NSObject元类。
NSObject元类的isa指针指向自己。
Person类的结构
{
-(void)init;
-(NSString*)getName;
}
Person元类的结构
{
-(void)alloc;
}
2.动态绑定
有时候有这样的需求,我们需要知道类中每个属性的名字,比如字典转模型。我们就可以通过runtime机制获取类中一些信息,包括属性列表,方法列表,遵循的协议列表等。这就类似java中的反射。
因为对象调用方法和类调用方法,实际上相当于给对应的target发送消息。那么我们就可以把消息封装成一个字符串,在代码执行(不是编译时)的时候,从服务器获取不同字符串来传递不同的消息,也就是调用不同的方法了,做到动态绑定。
3.runtime应用场景
runtime是一个iOS提供开发者的C语言库。
a.字典转模型
b.对象序列化和反序列化
把对象数据存到磁盘,把磁盘数据读取为对象,也就是对象的序列化和反序列化。
iOS对象序列化的思路是:
1.需要序列化的对象模型实现NSCoding协议。
2.实现NSCoding编码和解码这两个方法
-(void)encodeWithCoder:(NSCoder*)aCoder
-(instancetype)initWithCoder:(NSCoder*)aDecoder
比如把Person类对象进行序列化和反序列化。
@interfacePerson :Human
@property(nonatomic,copy)NSString* name;
@property(nonatomic,assign)intage;
@property(nonatomic,strong)NSMutableArray* phones;
-(instancetype)initWithCoder:(NSCoder*)aDecoder
{
self.name=[aDecoder decodeObjectForKey:@"name"];
self.age=[[aDecoder decodeObjectForKey:@"age"] intValue];
self.phones=[aDecoder decodeObjectForKey:@"phones"];
}
returnself;
}
-(void)encodeWithCoder:(NSCoder*)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:@(self.age) forKey:@"age"];
[aCoder encodeObject:self.phones forKey:@"phones"];
}
那么一定会面临几个问题,如果属性太多,难道需要些很多冗余代码么?父类的属性如何序列化?属性是复杂对象类型如何处理?
使用RunTime解决第一,二个问题。
-(instancetype)initWithCoder:(NSCoder*)aDecoder
{
if(self=[superinit])
{
Classcls =self.class;
//递归该对象的属性和父类中属性
while(cls!=[NSObjectclass]) {
unsignedintcount;
//通过runtime获取成员变量列表,该方法返回值是一个Ivar数组,Ivar是一个指向成员变量的指针
Ivar* ivarsList =class_copyIvarList(cls, &count);
for(inti=0; i
Ivarivar = ivarsList[i];
constchar* name =ivar_getName(ivar);
NSString* keyName = [NSStringstringWithUTF8String:name];
NSLog(@"class:%@,ivar:%@",cls,keyName);
idobj = [aDecoderdecodeObjectForKey:keyName];
//KVC赋值数据(经测试,KVC可以实现父类成员变量的获取和赋值)
[selfsetValue:objforKey:keyName];
}
//ivarsList需要被释放
free(ivarsList);
cls =class_getSuperclass(cls);
}
}
returnself;
}
-(void)encodeWithCoder:(NSCoder*)aCoder
{
Classcls =self.class;
while(cls!=[NSObjectclass]) {
unsignedintcount;
Ivar* ivarsList =class_copyIvarList(cls, &count);
for(inti=0; i
Ivarivar = ivarsList[i];
constchar* name =ivar_getName(ivar);
NSString* keyName = [NSStringstringWithUTF8String:name];
NSString* keyValue = [selfvalueForKey:keyName];
NSLog(@"class:%@,ivar:%@",cls,keyName);
[aCoderencodeObject:keyValueforKey:keyName];
}
free(ivarsList);
cls =class_getSuperclass(cls);
}
}
c.方法交换 Method Swizzling
交换两个方法的实现,实现面向切面变成的思想,一般用于给系统方法添加自定义实现。
eg:HooK出NSURL类方法URLWithString:和实例方法initWithString:并进行打印。
@implementation NSURL (ZS)
//在类第一次装载的时候进行方法交换,调用的时候才会交换,实现runtime效果
+(void)load
{
//交换类方法
Method class_origin_Method = class_getClassMethod(self.class, @selector(URLWithString:));
Method class_swizzling_Method = class_getClassMethod(self.class, @selector(ZS_URLWithString:));
method_exchangeImplementations(class_origin_Method, class_swizzling_Method);
//交换实例方法
Method instance_origin_Method = class_getInstanceMethod(self.class, @selector(initWithString:));
Method instance_swizzling_Method = class_getInstanceMethod(self.class, @selector(ZS_initWithString:));
method_exchangeImplementations(instance_origin_Method, instance_swizzling_Method);
}
+ (nullable instancetype)ZS_URLWithString:(NSString *)URLString
{
NSLog(@"ZS_URLString:%@",URLString);
return [NSURL ZS_URLWithString:URLString];
}
-(instancetype)ZS_initWithString:(NSString *)URLString
{
NSLog(@"ZS_initWithString:%@",URLString);
return [self ZS_initWithString:URLString];
}
- (void)viewDidLoad {
[super viewDidLoad];
//NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
NSURL * url = [[NSURL alloc]initWithString:@"http://www.qq.com"];
NSURLRequest * req = [NSURLRequest requestWithURL:url];
NSLog(@"req:%@",req);
}