#import "ViewController.h" /* Runtime(交换方法):主要想修改系统的方法实现 需求: 好比说有一个项目,已经开发了2年,突然项目负责人添加一个功能,每次UIImage加载图片,告诉我是否加载成功 当系统提供的控件不能知足咱们的需求的时候,咱们能够 1:经过继承系统控件,重写系统的方法,来扩充子类的行为(super的调用三种状况) 2:当须要为系统类扩充别的属性或是方法的时候,与哪一个类有关系,就为哪一个类建立分类。3:利用runtime修改系统的类,增长属性,交换方法,消息机制,动态增长方法 解决方法:1:重写系统的方法:新建类继承系统的类,重写系统的方法(要是覆盖父类的行为就不须要调用super,或是在super方法之下调用:在保留父类super原有的行为后,扩充子类本身的行为,代码写在super之上,能够修改super要传递的参数,例如重写setframe,要是想保留父类的行为就不要忘记调用super)。弊端:须要在每一个类中都须要引入头文件 2:写分类:为哪一个系统的类扩充属性和方法,就为哪一个类写分类 3:利用runtime底层的实现来修改或是访问系统的类:增长属性,交换方法,消息机制,动态增长方法 3:本需求利用runtime:不须要导入头文件,调用的仍是系统类原来的方法,只是利用了runtime的交换方法。 给系统的imageNamed添加功能,只能使用runtime(交互方法) 1.给系统的方法添加分类 2.本身实现一个带有扩展功能的方法 3.交互方法,只须要交互一次, 1.自定义UIImage 2.UIImage添加分类 */ @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // imageNamed => xmg_imageNamed 交互这两个方法实现 UIImage *image = [UIImage imageNamed:@"1.png"]; } @end
#import "UIImage+Image.h" #import <objc/message.h> /** * 总结: 1: + (void)load与+ (void)initialize的区别:+ (void)load:当类加载进内存的时候调用,并且无论有没有子类,都只会调用一次,在main函数以前调用,用途:1:能够新建类在该类中实现一些配置信息 2:runtime交换方法的时候,由于只须要交换一次方法,全部能够在该方法中实现交换方法的代码,用于只实现一次的代码 2:+ (void)initialize:当类被初始化的时候调用,可能会被调用屡次,如果没有子类,则只会调用一次,如果有子类的话,该方法会被调用屡次,如果子类的继承关系,先会调用父类的+ (void)initialize方法,而后再去调用子类的+ (void)initialize方法(如果继承关系,调用某个方法的时候,先会去父类中查找,如果父类中没有方法的实现就去子类中查找) 用途:1:在设置导航栏的全局背景的时候,只须要设置一次,能够重写该方法设置,最好是在该方法判断子类,如果本身,则实现设置全局导航栏的方法,若不是本身则跳过实现。2:在建立数据库代码的时候,能够在该方法中去建立,保证只初始化一次数据库实例,也能够用dispatch或是懒加载的方法中初始化数据库实例,也能保证只初始化一次数据库实例。其中也能够在+ (void)initialize方法中用dispatch也能保证即便有子类也只会初始化一次 2:交换方法:1:获取某个类的方法:class_getClassMethod:第一个参数:获取哪一个类的方法 第二个参数:SEL:获取哪一个方法 Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:)); // 交互方法:runtime method_exchangeImplementations(imageNamedMethod, xmg_imageNamedMethod); 也就是外部调用xmg_imageNamed就至关于调用了imageNamed,调用imageNamed就至关于调用了xmg_imageNamed 3:在分类中,最好不要重写系统方法,一旦重写,把系统方法实现给干掉,由于分类不是继承父类,而是继承NSObject,super没有改类的方法,因此就直接覆盖掉了父类的行为 */ @implementation UIImage (Image) // 把类加载进内存的时候调用,只会调用一次 + (void)load { // self -> UIImage // 获取imageNamed // 获取哪一个类的方法 // SEL:获取哪一个方法 Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:)); // 获取xmg_imageNamed Method xmg_imageNamedMethod = class_getClassMethod(self, @selector(xmg_imageNamed:)); // 交互方法:runtime method_exchangeImplementations(imageNamedMethod, xmg_imageNamedMethod); // 调用imageNamed => xmg_imageNamedMethod // 调用xmg_imageNamedMethod => imageNamed } // 会调用屡次 //+ (void)initialize //{ // static dispatch_once_t onceToken; // dispatch_once(&onceToken, ^{ // // }); // //} // 在分类中,最好不要重写系统方法,一旦重写,把系统方法实现给干掉 //+ (UIImage *)imageNamed:(NSString *)name //{ // // super -> 父类NSObject // //} // 1.加载图片 // 2.判断是否加载成功 + (UIImage *)xmg_imageNamed:(NSString *)name { // 图片 UIImage *image = [UIImage xmg_imageNamed:name]; if (image) { NSLog(@"加载成功"); } else { NSLog(@"加载失败"); } return image; } @end
runtime 的实现原理:数据库
二:经过继承重写实现:每次都须要导入头文件,并且项目中不少地方都得须要修改函数
#import <UIKit/UIKit.h> @interface XMGImage : UIImage @end
#import "XMGImage.h" @implementation XMGImage // 重写方法:想给系统的方法添加额外功能 + (UIImage *)imageNamed:(NSString *)name { // 真正加载图片:调用super初始化一张图片 UIImage *image = [super imageNamed:name]; if (image) { NSLog(@"加载成功"); } else { NSLog(@"加载失败"); } return image; } @end
* 开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,而且保持原有的功能。spa
* 方式一:继承系统的类,重写方法.code
* 方式二:使用runtime,交换方法.blog
```继承
@implementation ViewController图片
- (void)viewDidLoad {内存
[super viewDidLoad];开发
// Do any additional setup after loading the view, typically from a nib.get
// 需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。
// 步骤一:先搞个分类,定义一个能加载图片而且能打印的方法+ (instancetype)imageWithName:(NSString *)name;
// 步骤二:交换imageNamed和imageWithName的实现,就能调用imageWithName,间接调用imageWithName的实现。
UIImage *image = [UIImage imageNamed:@"123"];
}
@end
@implementation UIImage (Image)
// 加载分类到内存的时候调用
+ (void)load
{
// 交换方法
// 获取imageWithName方法地址
Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
// 获取imageWithName方法地址
Method imageName = class_getClassMethod(self, @selector(imageNamed:));
// 交换方法地址,至关于交换实现方式
method_exchangeImplementations(imageWithName, imageName);
}
// 不能在分类中重写系统方法imageNamed,由于会把系统的功能给覆盖掉,并且分类中不能调用super.
// 既能加载图片又能打印
+ (instancetype)imageWithName:(NSString *)name
{
// 这里调用imageWithName,至关于调用imageName
UIImage *image = [self imageWithName:name];
if (image == nil) {
NSLog(@"加载空的图片");
}
return image;
}
@end
```