iOS 运行时Runtime
Runtime能做非常非常多的事情,但是不能为了使用runtime而使用,因为使用runtime会使代码的阅读性降低,使用也不方便。只能在不得已的情况下使用。
以下介绍几个用法。
导入#import <objc/message.h> 或者 #import <objc/runtime.h>
一、发消息objc_msgSend();
a、获取一个类 objc_getClass();
b、注册一个方法 sel_registerName();
c、获取一个方法 sel_getUid();
//创建一个对象 //NSObject * obj = [NSObject alloc]; //id obj = objc_msgSend([NSObject class], @selector(alloc)); //发生消息,调用alloc方法,和上面两句代码都是等价的,他的作用都是一样 id obj = objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc")); //obj = [obj init]; //obj = objc_msgSend(obj, @selector(init)); //发生消息,调用init方法,和上面两句代码都是等价的,他的作用都是一样 obj = objc_msgSend(obj, sel_registerName("init"));/* objc_getClass("类名") 获取一个类 sel_registerName("方法名") 注册一个方法 */
二、方法交换
这里拿UIImage为例,添加一个UIImage的分类UIImage+jh.m
重新类方法+load
+(void)load{ //根据类获取类方法imageNamed Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:)); //根据类获取类方法xg_imageNamed Method xg_imageNamedMethod = class_getClassMethod(self, @selector(xg_imageNamed:)); //交换方法 method_exchangeImplementations(imageNamedMethod, xg_imageNamedMethod); }
写一个交换的方法xg_imageName:
+(UIImage *)xg_imageNamed:(NSString *)name{ //这里调用xg_imageNamed: 不是调用当前这个方法,因为我们在load中已经交换了imageNamed:这个方法,所以,这里调用xg_imageNamed:就会调用imageNamed:方法。 UIImage * image = [UIImage xg_imageNamed:name]; if (image) { NSLog(@"%@图片加载成功!",name); }else{ NSLog(@"%@图片加载失败!",name); } return image; };
所以我们在使用系统方法imageNamed:的时候就会调用xg_imageNamed:这个方法。
UIImage * image = [UIImage imageNamed:@"1.png"];
这样能够判断一个图片是否加载成功,而不需要修改系统方法,也不需要修改其他代码,方便扩展和开发。
三、动态添加属性
比如我需要给系统的NSObject类添加一个属性,那么大家都会想到使用分类添加一个属性,然后手动写setting和getting方法。
使用runtime也是一样,不过在写setting和getting方法的时候不太一样,使用runtime会方便非常多。比如加一个name的方法
添加一个NSObject+pro分类
NSObject+pro.h
#import <Foundation/Foundation.h> @interface NSObject (pro) /** 添加一个name属性 */ @property NSString * name; @end
NSObject+pro.m
#import "NSObject+pro.h" #import <objc/message.h> @implementation NSObject (pro) //写setting方法 -(void)setName:(NSString *)name{ //设置一个关联 ,因为我们所有的属性其实就是一个关联 //objc_setAssociatedObject(给哪个对象添加关联, @"关联名称", 关联的值, 修饰(我这里name是一个字符串,所有使用了copy)); objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC); } //写getting方法 -(NSString *)name{ return objc_getAssociatedObject(self, @"name"); } @end
使用的时候就可以直接.name了
NSObject * object = [[NSObject alloc]init]; object.name = @"123"; NSLog(@"name:%@",object.name);
四、动态添加方法
创建一个Person : NSObject 类
在viewDidLoad方法执行以下代码
Person * p = [[Person alloc]init]; //这里调用eat:方法,但是在person中并没有这个方法,而在运行的使用动态创建 [p performSelector:@selector(eat:) withObject:@"apple"]; //这里调用run方法并且返回一个结果,但是在person中并没有这个方法,而在运行的使用动态创建 NSNumber * i = [p performSelector:@selector(run)]; NSLog(@"p run :%@",i);
那么来看看是怎么动态添加
person.h文件是空的,什么都没有。
#import <Foundation/Foundation.h> @interface Person : NSObject @end
主要看person.m文件
#import "Person.h" #import <objc/message.h> @implementation Person //eat:方法的实现 void tEat(id self , SEL _cmd ,NSString * e){ NSLog(@"Person eat :%@",e); } //run方法的实现 NSNumber * tRun(id self , SEL _cmd){ return @100; } //但运行的时候,找不到某个方法就会执行 +(BOOL)resolveInstanceMethod:(SEL)sel{ //判断是不是找不到eat:方法 if (sel == NSSelectorFromString(@"eat:")) { /* 添加方法 */ //class :给谁(那个类)添加方法 //SEL :添加什么方法 //imp :方法的实现 -》函数 - 》 函数入口 - 》 函数名 //type :方法类型 方法类型怎么描述查看开发文档 Objective-C Runtime Programming Guide > Type Encodings.可以按住option键点击下面class_addMethod()函数,在方法说明里面用“不是黑色的字体”描述,可以直接跳转到对应说明 class_addMethod(self, sel, (IMP)tEat, "v@:@"); return YES; } //判断是不是找不到run 方法 if (sel == NSSelectorFromString(@"run")) { class_addMethod(self, sel, (IMP)tRun, "@@:"); return YES; } return [super resolveInstanceMethod:sel]; } @end
五、获取成员变量和类型
添加一个分类 NSObject+runtime
NSObject+runtime.h文件
#import <Foundation/Foundation.h> @interface NSObject (runtime) //获取所有的成员变量 +(instancetype)getAllIvar; @end
NSObject+runtime.m文件
#import "NSObject+runtime.h" #import <objc/message.h> @implementation NSObject (runtime) +(instancetype)getAllIvar{ id obj = [[self alloc]init]; unsigned int outCount = 0; //获取成员变量列表 Ivar * varList = class_copyIvarList(self, &outCount); for (int i = 0; i< outCount; i++) { Ivar ivar = varList[i]; //获取成员变量名 NSString * ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; ivarName = [ivarName substringFromIndex:1]; NSLog(@"成员变量名称:%@",ivarName); //给属性赋值,value不可以为nil id value; //现在假设value 不为nil [obj setValue:value forKey:ivarName]; //获取成员变量类型 NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""]; ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""]; //跟成员变量类型 得到对应的类 Class class = NSClassFromString(ivarType); NSLog(@"成员变量的类型是:%@",class); } return obj; } @end