Runtime

前提工作导入#import

runtime的使用场景:  1.  消息机制 ; 2. 交换方法  ;  3.动态添加方法

; 4. 动态添加属性;

任何方法的调用本质: 就是发送一个消息,用runtime发送消息,

*  //类方法的本质: 类对象调用[NSObject class]

1.  消息机制 :

*  1. 不得不用消息机制, 可以帮助开发者调用私有方法;

*  2. 修改系统方法的实现的时候

//id : 谁发送消息

//SEL : 发送什么消息

//Xcod6 : 使用objc_msgSend() 有参数提示, xcod6之后没有参数提示(打开提示build settings --> 搜索msg, --> 设置为No),原因是苹果不想让开发者了解runtime的底层实现,

//最终代码需要把当前代码重新编译,用xcod,  clang命令 ----->     clang -rewrite-objc main.m

id objc = objc_msgSend([NSObject class], @selector(alloc)); //相当于 id objc = [NSObject alloc]

objc = objc_msgSend(objc , @selector(init)); //相当于  objc =[objc init];

创建一个Person类

#import@interface Person : NSObject

- (void)eat;

- (void)run:(NSInteger)metre;

@end

#import "Person.h"#import@implementation Person

- (void)eat{

NSLog(@"吃");

}

- (void)run:(NSInteger)metre{

NSLog(@"metre = %ld",(long)metre);

}

@end

Person * p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc")); //相当于 Person * p  =[Person alloc];

p =  objc_msgSend(p,  sel_registerName("init")); // 相当于p = [p init];

//调用eat方法;

如果将eat方法注释掉 用OC的[p eat]会报错,只能用runtime的方法调用私有方法, runtime底层会自己查找eat方法

objc_msgSend(p, @selector(eat)); //不带参数 [p eat];

objc_msgSend(p, @selector(run:),20); //带参数的  [p run:20];

//方法的调用流程:(以eat方法为列)

//怎么调用(eat)方法: -->对象方法: 保存到类对象的方法列表(MethodList)里面 ;  类方法: 保存在元类中的方法列表(MethodList)中

// 1. 通过isa指针到对应的类中查找,

// 2. 把方法名注册为方法编号(_cmd)

// 3. 根据方法编号去查找对应的方法(而不是根据方法名去查找对应的方法)

// 4. 找到的只是最终函数实现地址,根据地址到方法区(内存)调用对应的函数(方法的实现不是保存子在类里面而是在内存的方法区)


2. runtime(交换方法):

*  需求: 每次UIImage加载图片, 告诉是否加载成功 (给系统的imageNamed添加方法)

// 给系统的imageNamed添加方法 : 1. 给系统的方法添加分类 2. 自己实现带有扩展功能的方法 3 . 交换方法, 交换一次就可以了

*  1. 自定义UIImage ( 每次使用都需要导入自定义的类, 2 .项目太大,没办法实现  )

*  2. 分类 (分类中尽最好不要重写系统的方法, 一旦重写就会把系统的方法实现干掉)

#import@interface UIImage (category)

+ (UIImage *)my_imageNamed:(NSString *)name;

@end

#import "UIImage+category.h"#import@implementation UIImage (category)

//把类加载进内存的时候调用, 只会调用一次

+ (void)load{  // swift中只能用initialize方法

//获取imageNamed方法:

Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));

//获取my_imageNamed方法:

Method my_imageNamedMethod = class_getClassMethod(self, @selector(my_imageNamed:));

// 交换方法: runtime

method_exchangeImplementations(imageNameMethod, my_imageNamedMethod);

}

//

+ (UIImage *)my_imageNamed:(NSString *)name{

UIImage * image = [UIImage my_imageNamed:name];

if (image) {

NSLog(@"加载成功");

}else{

NSLog(@"加载失败");

}

return image;

}

- (void)viewDidLoad {

[super viewDidLoad];

UIImage * image = [UIImage imageNamed:@"1.png"];//调用imageNamed时候 ==> 调用my_imageNamed方法

}

3.动态添加方法

runtime(动态添加方法): OC是懒加载机制,只要一个方法实现了, 就会马上添加到方法列表中.

performSelector什么使用, 动态添加方法的时候使用, 为什么动态的添加方法(对收费版的APP需要用到runtime动态添加方法)

创建Car类

#import@interface Car : NSObject

@property (assign, nonatomic) NSInteger  tel;

@property (strong, nonatomic) NSString * sex;

@end

#import "Car.h"#import@implementation Car

//方法 (函数)

void abc(id self, SEL _cmd) {

NSLog(@"红旗");

}

//任何方法默认都有两个隐式参数: self , _cmd

//只要一个对象调用了一个未实现的方法就会调用这个方法,进行处理

// 作用: 动态添加方法, 处理未实现的方法

+ (BOOL)resolveInstanceMethod:(SEL)sel{

if (sel == NSSelectorFromString(@"name")) {

/**

*  @param Class : 给那个类添加方法

*  @param SEL :  添加那个方法

*  @param IMP :  方法实现 --> 函数 --> 函数入口 --> 函数名

*  @param type : 方法类型

*  @return

*/

class_addMethod(self, sel, (IMP)abc, "v@:");

return YES;

}

return [super resolveInstanceMethod:sel];

}

@end

- (void)viewDidLoad {

[super viewDidLoad];

Car * car = [[Car alloc]init];

[car performSelector:@selector(name)];

}



4. 动态添加属性

Runtime(动态添加属性): 什么情况下需要动态添加属性(给系统的类添加属性的时候, 可以用runtime动态添加属性的方法)

*  需求:让一个NSObject 类保存一个字符串

*  动态添加属性的本质: 让某个属性与对象产生关联

*  runtime一般都是针对系统的类,

#import 

@interface NSObject (propertyCategory)

//@property在分类里面的只会产生get,set方法的声明,不会生成实现,也不会生成下划线成员属性

@property NSString * name;

@end

#import "NSObject+propertyCategory.h"

#import@implementation NSObject (propertyCategory)

- (void)setName:(NSString *)name{//让这个字符串与当前对象产生联系

// object : 给哪个对象添加属性   ;   key : 属性名称   ; value : 属性值    policy : 保存策略

objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}

- (NSString *)name{

return objc_getAssociatedObject(self, @"name");

}

@end

- (void)viewDidLoad {

[super viewDidLoad];

NSObject * objc = [[NSObject alloc]init];

objc.name = @"789";//动态给NSObject添加name属性

NSLog(@"%@",objc.name);

}

你可能感兴趣的:(Runtime)