什么是runtime?
1->runtime是一套底层的C语言API(包含很多强大实用的C语言数据类型、C语言函数)
2->实际上,平时我们编写的OC代码,底层都是基于runtime实现的
也就是说,平时我们编写的OC代码,最终都是转成了底层的runtime代码(C语言代码)
runtime有啥用?
1> 能动态产生一个类、一个成员变量、一个方法
2> 能动态修改一个类、一个成员变量、一个方法
3> 能动态删除一个类、一个成员变量、一个方法
常见的函数、头文件
#import <objc/runtime.h> : 成员变量、类、方法
Ivar * class_copyIvarList : 获得某个类内部的所有成员变量
Method * class_copyMethodList : 获得某个类内部的所有方法
Method class_getInstanceMethod : 获得某个实例方法(对象方法,减号-开头)
Method class_getClassMethod : 获得某个类方法(加号+开头)
method_exchangeImplementations : 交换2个方法的具体实现
#import <objc/message.h> : 消息机制
objc_msgSend(....)
首先自定义一个类型
@interface LSPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property(nonatomic,assign) int age;
@end
- (void)viewDidLoad {
[super viewDidLoad];
unsigned int count;
Ivar *ivars= class_copyIvarList([LSPerson class], &count);
for (int i=0; i<count ; i++) {
const char *name= ivar_getName(ivars[i]);
const char *type=ivar_getTypeEncoding(ivars[i]);
NSLog(@"name:%s type:%s",name,type);
}
NSLog(@"count:%d",count);
unsigned int methodCount;
Method *methods =class_copyMethodList([LSPerson class], &methodCount);
for (int i=0; i<methodCount ; i++) {
NSString *methodName= NSStringFromSelector( method_getName(methods[i]));
NSLog(@"method:%@",methodName);
}
NSLog(@"methodCount:%d",methodCount);
}
2015-11-04 13:02:58.023 runtime[16409:538012] name:_name type:@"NSString"
2015-11-04 13:02:58.024 runtime[16409:538012] name:_age type:i
2015-11-04 13:02:58.024 runtime[16409:538012] count:2
2015-11-04 13:02:58.024 runtime[16409:538012] method:age
2015-11-04 13:02:58.024 runtime[16409:538012] method:setAge:
2015-11-04 13:02:58.024 runtime[16409:538012] method:.cxx_destruct
2015-11-04 13:02:58.024 runtime[16409:538012] method:name
2015-11-04 13:02:58.024 runtime[16409:538012] method:setName:
2015-11-04 13:02:58.025 runtime[16409:538012] count:5
利用runtime来实现交换两个方法,
如果公司的项目做了很久突然要改需求,做某系统的适配,这时候显示的图片就要换了,但是以前的代码又太多没法改,所以我们只能把系统方法换掉,利用如下方法即可实现
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
@implementation UIImage (Extension)
+(void)load
{
Method originMehtod = class_getClassMethod(self, @selector(imageNamed:));
Method newMehtod = class_getClassMethod(self, @selector(imageWithName:));
// 交换2个方法的实现
method_exchangeImplementations(newMehtod, originMehtod);
}
+(UIImage *)imageWithName:(NSString *)name
{
BOOL IOS8=[[UIDevice currentDevice].systemVersion doubleValue]>=8.0;
UIImage *image=nil;
if (IOS8) {
NSString *newName=[name stringByAppendingString:@"_os8"];
image=[self imageWithName:newName];
}
if (image==nil) {
[self imageWithName:name];
}
return image;
}
@end
在调试时我们就经常会遇到objectAtIndex bounds 错误就是下标越界了,还有一种情况就是object can not be nil就是不能往数组里添加nil这时候就需要替换系统自带的方法
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@implementation NSObject (Extension)
/**
*交换某个类的对象方法
*/
+(void)swizzleInstanceMethod:(Class)class originalSelector:(SEL)originalSelector newSelector:(SEL)newSelectoer
{
Method originalMethod=class_getInstanceMethod(class, originalSelector);
Method newMethod=class_getInstanceMethod(class, newSelectoer);
method_exchangeImplementations(originalMethod, newMethod);
}
/**
*交换某个类的类方法
*/
+(void)swizzleClassMethod:(Class)class originalSelector:(SEL)originalSelector newSelector:(SEL)newSelectoer
{
Method originalMethod=class_getClassMethod(class, originalSelector);
Method newMethod=class_getClassMethod(class, newSelectoer);
method_exchangeImplementations(originalMethod, newMethod);
}
@end
@implementation NSMutableArray(Extension)
/**
*当程序启动时会把所有的类都加载到内存中,只会调用一次load方法
*/
+(void)load
{
[self swizzleInstanceMethod:NSClassFromString(@"__NSArrayM") originalSelector:@selector(addObject:) newSelector:@selector(ls_AddObject:)];
[self swizzleInstanceMethod:NSClassFromString(@"__NSArrayM") originalSelector:@selector(objectAtIndex:) newSelector:@selector(ls_ObjectAtIndex:)];
}
-(id)ls_ObjectAtIndex:(NSUInteger)index
{
if (index<self.count) {
return [self ls_ObjectAtIndex:index];//注意一下所有方法里都不会造成循环引用,因为此时的方法已经替换成了系统的<span style="font-family: Arial, Helvetica, sans-serif;">objectAtIndex:方法</span>
}else{
return nil;
}
}
-(void)ls_AddObject:(id)obj
{
if (obj!=nil) {
[self ls_AddObject:obj];
}
}
@end
@implementation NSArray(Extension)
+(void)load
{
[self swizzleInstanceMethod:NSClassFromString(@"__NSArrayI") originalSelector:@selector(objectAtIndex:) newSelector:@selector(ls_ObjectAtIndex:)];
}
-(id)ls_ObjectAtIndex:(NSUInteger)index
{
if (index<self.count) {
return [self ls_ObjectAtIndex:index];
}else{
return nil;
}
}
@end