天天看點

Runtime 一一 runtime使用-動态添加方法和屬性

一、動态添加方法

應用場景: 當一個類的方法非常多,加載類到記憶體的時候比較耗資源,需要給每個方法生成映射表. 此時可以動态給某個類添加方法

面試題: 是否使用過performSelector, 其實就是想問你有沒有動态添加過方法

知識點:

1. 所有的方法,預設都有 id self, SEL _cmd 這兩個隐式參數. _cmd表示目前方法的方法編号

2. + (BOOL)resolveInstanceMethod:(SEL)sel 用來判斷,未實作的方法是否是我們想要動态添加的方法

3. 動态添加 class_addMethod(arg1, arg2, arg3, arg4);

#import "ViewController.h"
#import "Person.h"

@interface ViewController ()

@end

@implementation ViewController

/*
    動态添加方法:
    開發中的使用場景: 如果一個類方法非常多,加載類到記憶體的時候比較消耗資源,需要給每個方法生成映射表,可以動态給某個類添加方法
    面試題: 是否使用過performSelector, 其實主要想問你有沒有動态添加過方法
 */

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *p = [[Person alloc] init];
//    [p performSelector:@selector(eat)];
    [p performSelector:@selector(run:) withObject:@"ZY" withObject:@20];
    
}

@end
           
#import "Person.h"
#import <objc/message.h>

@implementation Person

// 所有的方法.預設都有self, _cmd 這兩個隐式參數
// _cmd 表示目前方法的方法編号
void eatFood(id self, SEL _cmd)
{
    NSLog(@"吃食物!");
}

void runPace(id self, SEL _cmd, NSString *name, NSNumber *meter)
{
    NSLog(@"我叫%@,我今年%@,我在?",name,meter);
    // _cmd 表示目前方法的方法編号
    NSLog(@"%@",NSStringFromSelector(_cmd));
}

// 當一個對象調用未實作的方法,會調用這個方法來處理,并且會把對應的方法清單傳過來
// 可以用來判斷,未實作的方法是否是我們想要動态添加的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
//    if ([NSStringFromSelector(sel) isEqualToString:@"eat"]){
    if (sel == NSSelectorFromString(@"eat")) {
        /*
         param1: class 給哪個類添加方法
         param2: SEL 添加哪個方法
         param3: IMP: 方法實作(在方法區) => 函數 => 函數入口 => 函數名
         param4: type: 方法類型
         */
        class_addMethod(self, sel, (IMP)eatFood, "[email protected]:");
        
        return YES;
    }
    if ([NSStringFromSelector(sel) isEqualToString:@"run:"]) {
        
        class_addMethod(self, sel, (IMP)runPace, "[email protected]:@@");
        
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
           

二、動态添加屬性

應用場景: 給系統的類增加屬性的時候,可以使用runtime動态添加屬性.

本質: 動态添加屬性,就是讓某個屬性與對象産生關聯

runtime一般針對系統的類

需求: 把基類NSObject類,添加一個name字元串屬性

步驟:

1. 給系統自帶的NSObject屬性添加分類

2. 在分類中聲明name屬性,在分類中@property NSString *name; 隻會生成get,set方法的聲明,并且不會生成私有屬性_name.

本質不會生成成員屬性,而是提供get,set方法聲明,供外界調用

3. 在分類中實作get,set方法

4. set方法實作,把傳遞的值給對象關聯

5. get方法實作,把關聯的值取出來傳出去

#import "ViewController.h"
#import "NSObject+Property.h"
#import "Person.h"

@interface ViewController ()

@end

@implementation ViewController

// 動态添加屬性
// 開發場景:
// 給系統的類添加屬性的時候,可以使用runtime動态添加屬性方法
// 本質:動态添加屬性,就是讓某個屬性與對象産生關聯
// runtime一般都是針對系統的類

/*
    需求:讓一個NSObject類 儲存一個name字元串屬性
    動态添加NSObject屬性的步驟
    1.給系統自帶的NSObject添加分類
    2.在分類中聲明屬性的get,set方法,本質并不是真的生成成員屬性,而是提供get,set方法,供外界擷取
    3.在分類實作get,set方法
    4.set方法實作,把傳遞的值給對象關聯。
    5.get方法實作,把關聯的值取出來傳出去
 */

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSObject *objc = [[NSObject alloc] init];
    objc.name = @"ZY";
    NSLog(@"%@",objc.name);
    
    Person *p = [[Person alloc] init];
    p.name = @"GZY";
    
}
@end
           

分類NSObject+Property:

#import <Foundation/Foundation.h>

@interface NSObject (Property)

// @property分類: 隻會生成get,set方法聲明,不會生成方法實作,并且不會生成_name私有屬性
@property NSString *name;

@end

#import "NSObject+Property.h"
#import <objc/message.h>

@implementation NSObject (Property)

// 屬性名稱
static const char *key = "name";

// set方法
- (void)setName:(NSString *)name
{
    // 讓這個字元串與目前對象産生聯系
    /*
     param1:object:給哪個對象添加屬性
     param2:key:屬性名稱
     param3:value:屬性值
     param4:policy:儲存政策
     */
    objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

// get 方法
- (NSString *)name
{
    return objc_getAssociatedObject(self, key);
}

@end
           

繼續閱讀