ios開發runtime學習二:runtime交換方法

#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

```

相關文章
相關標籤/搜索