#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
```