iOS開(kāi)發(fā):學(xué)習(xí)Runtime

學(xué)習(xí)iOS開(kāi)發(fā),runtime這個(gè)知識(shí)點(diǎn)是繞不過(guò)去的,但對(duì)于我這種學(xué)習(xí)OC不是太久,寫(xiě)OC的量不夠多的人來(lái)說(shuō),抽象理解runtime的概念或者是看源代碼有點(diǎn)枯燥,效果也不好,以例子的方法學(xué)習(xí)可能會(huì)更好,隨著代碼量的上升,對(duì)runtime的理解會(huì)越來(lái)越深入。
詳細(xì)代碼ARRuntimeDemo,開(kāi)發(fā)環(huán)境Xcode9.4

Person.h為:

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    NSString * firstName;
}
@property (nonatomic, assign) int age;

+(void)run;
+(void)study;

-(void)f1;
-(void)f2;

@end

Person.m為:

#import "Person.h"

@implementation Person
{
    NSString *lastname;  
    float weight;
}

-(instancetype)init {
    self = [super init];
    if (self) {
        firstName = @"Andy";
        
    }
    return self;
}

-(void)f1 {
    NSLog(@"執(zhí)行f1");
}

-(void)f2 {
    NSLog(@"執(zhí)行f2");
}

+ (void)run {
    NSLog(@"跑");
}

+ (void)study {
    NSLog(@"學(xué)習(xí)");
}

@end

1 獲取類的所有變量(包括成員變量和屬性變量)

// 1. 獲取所有變量,包括成員變量和屬性變量
- (IBAction)getAllVar:(UIButton *)sender {
    unsigned int count = 0;
    Ivar *allVariables = class_copyIvarList([Person class], &count);
    
    for (int i=0; i<count; i++) {
        Ivar ivar = allVariables[i];
        const char *Variablename = ivar_getName(ivar);
        const char *VariableType = ivar_getTypeEncoding(ivar);
        
        NSLog(@"Name: %s  Type: %s", Variablename, VariableType);
    }
}

結(jié)果輸出(其中firstName、lastname、weight為成員變量,_age為屬性變量):

2018-06-01 17:04:56.275194+0800 ARRuntimeDemo[60670:5448260] Name: firstName  Type: @"NSString"
2018-06-01 17:04:56.276120+0800 ARRuntimeDemo[60670:5448260] Name: lastname  Type: @"NSString"
2018-06-01 17:04:56.276503+0800 ARRuntimeDemo[60670:5448260] Name: weight  Type: f
2018-06-01 17:04:56.276614+0800 ARRuntimeDemo[60670:5448260] Name: _age  Type: i

解釋:

  • Iva,一個(gè)指向objc_ivar結(jié)構(gòu)體指針,包含了變量名、變量類型等信息
  • 像lastname、weight這種定義在@implementation所謂的私有變量也可獲取
  • 對(duì)應(yīng)class_copyIvarList還有一個(gè)class_copyPropertyList只能獲得屬性變量的方法

2 獲取所有方法(不包括類方法)

// 2. 獲取所有方法,不包括類方法
- (IBAction)getAllMethod:(UIButton *)sender {
    unsigned int count;
    //獲取方法列表,所有在.m文件顯式實(shí)現(xiàn)的方法都會(huì)被找到,包括setter+getter方法;
    Method *allMethods = class_copyMethodList([Person class], &count);
    for(int i =0;i<count;i++) {
        //Method,為runtime聲明的一個(gè)宏,表示對(duì)一個(gè)方法的描述
        Method md = allMethods[i];
        //獲取SEL:SEL類型,即獲取方法選擇器@selector()
        SEL sel = method_getName(md);
        //得到sel的方法名:以字符串格式獲取sel的name,也即@selector()中的方法名稱
        const char *methodname = sel_getName(sel);
        
        NSLog(@"(Method:%s)",methodname);
    }
}

結(jié)果輸出:

2018-06-01 16:54:50.433232+0800 ARRuntimeDemo[60482:5418318] (Method:f1)
2018-06-01 16:54:50.433465+0800 ARRuntimeDemo[60482:5418318] (Method:f2)
2018-06-01 16:54:50.433930+0800 ARRuntimeDemo[60482:5418318] (Method:.cxx_destruct)
2018-06-01 16:54:50.434335+0800 ARRuntimeDemo[60482:5418318] (Method:init)
2018-06-01 16:54:50.435163+0800 ARRuntimeDemo[60482:5418318] (Method:height)
2018-06-01 16:54:50.435788+0800 ARRuntimeDemo[60482:5418318] (Method:setHeight:)
2018-06-01 16:54:50.435990+0800 ARRuntimeDemo[60482:5418318] (Method:setAge:)
2018-06-01 16:54:50.436482+0800 ARRuntimeDemo[60482:5418318] (Method:age)

解釋:

  • 獲得了像height,setHeight這種隱藏的settergetter方法
  • Method是一個(gè)指向objc_method結(jié)構(gòu)體指針,表示對(duì)類中的某個(gè)方法的描述。
  • .cxx_destruct是關(guān)于系統(tǒng)自動(dòng)內(nèi)存釋放的隱藏方法

3 為類添加新屬性

category只能為類添加新方法,不能添加新屬性,但通過(guò)runtime配合category就可以達(dá)到添加屬性效果。
首先新建一個(gè)類Person的category:
.h文件

//  Person+Category.h

#import "Person.h"

@interface Person (Category)

@property (nonatomic, assign)float height;

@end

.m文件

//  Person+Category.m

#import "Person+Category.h"
#import <objc/runtime.h>

const char *key = "myKey";

@implementation Person (Category)

-(void)setHeight:(float)height {
    NSNumber *num = [NSNumber numberWithFloat:height];
    /*
     objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
     第一個(gè)參數(shù)是需要添加屬性的對(duì)象;
     第二個(gè)參數(shù)是屬性的key,是C字符串就可以;
     第三個(gè)參數(shù)是屬性的值,類型必須為id,所以此處height先轉(zhuǎn)為NSNumber類型;
     第四個(gè)參數(shù)是使用策略,是一個(gè)枚舉值,類似@property屬性創(chuàng)建時(shí)設(shè)置屬性修飾符,可從命名看出各枚舉的意義;
     */
    objc_setAssociatedObject(self, key, num, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(float)height {
    NSNumber *number = objc_getAssociatedObject(self, key);
    return [number floatValue];
}
@end

然后就能訪問(wèn)新屬性height了:

- (IBAction)addVar:(UIButton *)sender {
    per = [[Person alloc] init];
    
    per.height = 123;
    NSLog(@"%f", [per height]);
}

此時(shí)雖然通過(guò)上面的獲取所有變量方法不能獲取height,但通過(guò)上面的額獲取所有方法可以獲取heightsetHeight方法了:

2018-06-01 17:14:36.945742+0800 ARRuntimeDemo[60892:5482950] Name: firstName  Type: @"NSString"
2018-06-01 17:14:36.948330+0800 ARRuntimeDemo[60892:5482950] Name: lastname  Type: @"NSString"
2018-06-01 17:14:36.948771+0800 ARRuntimeDemo[60892:5482950] Name: weight  Type: f
2018-06-01 17:14:36.949166+0800 ARRuntimeDemo[60892:5482950] Name: _age  Type: i

2018-06-01 17:15:02.198444+0800 ARRuntimeDemo[60892:5482950] (Method:f1)
2018-06-01 17:15:02.198620+0800 ARRuntimeDemo[60892:5482950] (Method:f2)
2018-06-01 17:15:02.198800+0800 ARRuntimeDemo[60892:5482950] (Method:.cxx_destruct)
2018-06-01 17:15:02.198917+0800 ARRuntimeDemo[60892:5482950] (Method:init)
2018-06-01 17:15:02.199048+0800 ARRuntimeDemo[60892:5482950] (Method:height)
2018-06-01 17:15:02.199150+0800 ARRuntimeDemo[60892:5482950] (Method:setHeight:)
2018-06-01 17:15:02.199239+0800 ARRuntimeDemo[60892:5482950] (Method:setAge:)
2018-06-01 17:15:02.199356+0800 ARRuntimeDemo[60892:5482950] (Method:age)

4 添加新方法

// 4. 添加新方法
- (IBAction)addMethod:(UIButton *)sender {
    /* 動(dòng)態(tài)添加方法:
     第一個(gè)參數(shù)表示Class cls 類型;
     第二個(gè)參數(shù)表示待調(diào)用的方法名稱;
     第三個(gè)參數(shù)(IMP)myAddingFunction,IMP一個(gè)函數(shù)指針,這里表示指定具體實(shí)現(xiàn)方法myAddingFunction;
     第四個(gè)參數(shù)表方法的參數(shù),0代表沒(méi)有參數(shù);
     */
    class_addMethod([per class], @selector(NewMethod), (IMP)myAddingFunction, 0);
    //調(diào)用方法 【如果使用[per NewMethod]調(diào)用方法,在ARC下會(huì)報(bào)“no visible @interface"錯(cuò)誤】
    [per performSelector:@selector(NewMethod)];
}
//具體的實(shí)現(xiàn)(方法的內(nèi)部都默認(rèn)包含兩個(gè)參數(shù)Class類和SEL方法,被稱為隱式參數(shù)。)
int myAddingFunction(id self, SEL _cmd){
    NSLog(@"已新增方法:NewMethod");
    return 1;
}

輸出:

2018-06-01 17:31:56.113168+0800 ARRuntimeDemo[61295:5543319] 已新增方法:NewMethod

5 交換兩個(gè)方法

// 5. 交換兩個(gè)方法
- (IBAction)swapMethod:(UIButton *)sender {
    [Person run];
    [Person study];
    
    Method m1 = class_getClassMethod([Person class], @selector(run));
    Method m2 = class_getClassMethod([Person class], @selector(study));
    
    method_exchangeImplementations(m1, m2);
    
    [Person run];
    [Person study];
}

輸出:

2018-06-01 17:39:00.497375+0800 ARRuntimeDemo[61448:5566239] 跑
2018-06-01 17:39:00.497841+0800 ARRuntimeDemo[61448:5566239] 學(xué)習(xí)
2018-06-01 17:39:00.499255+0800 ARRuntimeDemo[61448:5566239] 學(xué)習(xí)
2018-06-01 17:39:00.499449+0800 ARRuntimeDemo[61448:5566239] 跑

這篇文章我只是做了runtime一些簡(jiǎn)單使用,并沒(méi)有相關(guān)的使用場(chǎng)景,算是入門(mén)了,文末參考提到的文章都是不錯(cuò),值得以后深入。

參考:
iOS開(kāi)發(fā) -- Runtime 的幾個(gè)小例子
OC最實(shí)用的runtime總結(jié),面試、工作你看我就足夠了!
iOS-RunTime,不再只是聽(tīng)說(shuō)
Runtime全方位裝逼指南
Objective-C Runtime

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,262評(píng)論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,674評(píng)論 1 32
  • 引導(dǎo) 對(duì)于從事 iOS 開(kāi)發(fā)人員來(lái)說(shuō),所有的人都會(huì)答出「 Runtime 是運(yùn)行時(shí) 」,什么情況下用 Runtim...
    Winny_園球閱讀 4,313評(píng)論 3 75
  • 在遇見(jiàn)對(duì)的人之前,練成金剛鉆石心? 又是一年櫻花開(kāi),還記得去年櫻花開(kāi)的正旺,我靜待春暖,你如期而至。你如春雨似甘霖...
    Charlotte_yue閱讀 261評(píng)論 0 0
  • 根據(jù)研究發(fā)現(xiàn),孩子在五歲到七歲之間,大腦前額葉會(huì)進(jìn)行發(fā)展和重組的一個(gè)過(guò)程,就是因?yàn)檫@些變化才讓元記憶的發(fā)展有了可能...
    瞧那一家子閱讀 686評(píng)論 0 0

友情鏈接更多精彩內(nèi)容