iOS Runtime的使用

要想了解Runtime,需要線了解一些本質(zhì)的知識



1.從上圖中,我們可以看出,我們常見的 類 其本質(zhì)就是一個(gè)結(jié)構(gòu)體

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

typedef struct objc_class *Class;

而Class 就是類的結(jié)構(gòu)體的指針

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;


改變一個(gè)對象所屬的類別

NSArray *tempAry = @[@"temp",@"temp2"];
NSLog(@"%@",tempAry);
object_setClass(tempAry,[NSObject class]);
NSLog(@"%@",tempAry);

相關(guān)的其他一些方法

Class object_getClass(id obj)     // 獲取對象的類
Class object_setClass(id obj, Class cos) // 更改對象的類為另一個(gè)類
BOOL object_isClass(id obj)   // 判斷傳進(jìn)來的是類結(jié)構(gòu)體還是Class結(jié)構(gòu)體
const char *object_getClassName(id obj) // 獲取傳進(jìn)來結(jié)構(gòu)體所屬的類
const char *class_getName(Class cls)  // 獲取Class的名字
BOOL class_isMetaClass(Class cls)   // 判斷是不是根Class
Class class_getSuperclass(Class cos) // 獲取Class的父Class
Class class_setSuperclass(Class cls, Class newSuper) // 設(shè)置Class的父類
int class_getVersion(Class cos) // 獲取類的版本信息
void class_setVersion(Class cls, int version) // 更改類的版本信息

類結(jié)構(gòu)體中不同屬性代表的意義

//類在runtime中的表示
 struct objc_class { 
    Class isa;//指針,顧名思義脂信,表示是一個(gè)什么碧囊, //實(shí)例的isa指向類對象陪捷,類對象的isa指向元類 
#if !__OBJC2__ 
    Class super_class; //指向父類 
    const char *name; //類名 
    long version; 
    long info; 
    long instance_size 
    struct objc_ivar_list *ivars //成員變量列表 
    struct objc_method_list **methodLists; //方法列表 
    struct objc_cache *cache;//緩存  //一種優(yōu)化糯景,調(diào)用過的方法存入緩存列表,下次調(diào)用先找緩存 
    struct objc_protocol_list *protocols //協(xié)議列表 
#endif 
} OBJC2_UNAVAILABLE; 
/* Use `Class` instead of `struct objc_class *` */

獲取屬性列表

unsigned int count;
 //獲取屬性列表 
objc_property_t *propertyList = class_copyPropertyList([self class], &count); 
for (unsigned int i=0; i<count; i++) {
    const char *propertyName = property_getName(propertyList[i]); 
    NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]); 
} 

獲取方法列表

unsigned int count;
//獲取方法列表 
Method *methodList = class_copyMethodList([self class], &count);
 for (unsigned int i; i<count; i++) {
     Method method = methodList[i]; 
     NSLog(@"method---->%@",NSStringFromSelector(method_getName(method))); 
} 

獲取成員變量列表

unsigned int count;
//獲取成員變量列表
 Ivar *ivarList = class_copyIvarList([self class], &count);
 for (unsigned int i; i<count; i++) { 
      Ivar myIvar = ivarList[i]; 
      const char *ivarName = ivar_getName(myIvar);
      NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]); 
} 

獲取協(xié)議列表

unsigned int count;
//獲取協(xié)議列表
 __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count); 
for (unsigned int i; i<count; i++) {
      Protocol *myProtocal = protocolList[i]; 
      const char *protocolName = protocol_getName(myProtocal); 
      NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
 }

方法調(diào)用

讓我們看一下方法調(diào)用在運(yùn)行時(shí)的過程(參照前文類在runtime中的表示)
如果用實(shí)例對象調(diào)用實(shí)例方法够颠,會(huì)到實(shí)例的isa指針指向的對象(也就是類對象)操作压语。如果調(diào)用的是類方法,就會(huì)到類對象的isa指針指向的對象(也就是元類對象)中操作忍啤。

  • 首先加勤,在相應(yīng)操作的對象中的緩存方法列表中找調(diào)用的方法,如果找到同波,轉(zhuǎn)向相應(yīng)實(shí)現(xiàn)并執(zhí)行鳄梅。
  • 如果沒找到,在相應(yīng)操作的對象中的方法列表中找調(diào)用的方法未檩,如果找到戴尸,轉(zhuǎn)向相應(yīng)實(shí)現(xiàn)執(zhí)行
  • 如果沒找到,去父類指針?biāo)赶虻膶ο笾袌?zhí)行1冤狡,2.
    以此類推孙蒙,如果一直到根類還沒找到项棠,轉(zhuǎn)向攔截調(diào)用。
  • 如果沒有重寫攔截調(diào)用的方法马篮,程序報(bào)錯(cuò)沾乘。

攔截調(diào)用



當(dāng)調(diào)用一個(gè)不存在的類方法或者實(shí)例方法的時(shí)候,會(huì)走下面兩個(gè)方法

+ (BOOL)resolveClassMethod:(SEL)sel; 
+ (BOOL)resolveInstanceMethod:(SEL)sel; 

將你調(diào)用的不存在的方法重定向到一個(gè)其他聲明了這個(gè)方法的類,只需要你返回一個(gè)有這個(gè)方法的target


- (id)forwardingTargetForSelector:(SEL)aSelector;

將你調(diào)用的不存在的方法打包成NSInvocation傳給你浑测。做完你自己的處理后翅阵,調(diào)用invokeWithTarget:方法讓某個(gè)target觸發(fā)這個(gè)方法

- (void)forwardInvocation:(NSInvocation *)anInvocation;

動(dòng)態(tài)添加方法

//隱式調(diào)用方法
[target performSelector:@selector(resolveAdd:) withObject:@"test"];

配合使用

void runAddMethod(id self, SEL _cmd, NSString *string){
 NSLog(@"add C IMP ", string); 
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    //給本類動(dòng)態(tài)添加一個(gè)方法
      if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {
            class_addMethod(self, sel, (IMP)runAddMethod, "v@:*"); 
     } 
return YES;
 }

BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)

  1. Class cls 給哪個(gè)類添加方法,本例中是self
  2. SEL name 添加的方法迁央,本例中是重寫的攔截調(diào)用傳進(jìn)來的selector掷匠。
  3. IMP imp 方法的實(shí)現(xiàn),C方法的方法實(shí)現(xiàn)可以直接獲得岖圈。如果是OC方法讹语,可以用+ (IMP)instanceMethodForSelector:(SEL)aSelector;獲得方法的實(shí)現(xiàn)。
  4. "v@:*"方法的簽名蜂科,代表有一個(gè)參數(shù)的方法顽决。


給類添加方法(關(guān)聯(lián)對象)

objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)
objc_getAssociatedObject(<#id object#>, <#const void *key#>)

屬性的幾種特征

enum { 
      OBJC_ASSOCIATION_ASSIGN = 0,
      OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
      OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
      OBJC_ASSOCIATION_RETAIN = 01401,
      OBJC_ASSOCIATION_COPY = 01403
 };

_cmd代表當(dāng)前調(diào)用方法的地址。getAssociatedObject方法的地址作為唯一的key

//添加關(guān)聯(lián)對象
 - (void)addAssociatedObject:(id)object{
      objc_setAssociatedObject(self, @selector(getAssociatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}
 //獲取關(guān)聯(lián)對象 
- (id)getAssociatedObject{
       return objc_getAssociatedObject(self, _cmd);
 }

交換方法的實(shí)現(xiàn)

//load方法會(huì)在類第一次加載的時(shí)候被調(diào)用
//調(diào)用的時(shí)間比較靠前导匣,適合在這個(gè)方法里做方法交換
+ (void)load{
    //方法交換應(yīng)該被保證才菠,在程序中只會(huì)執(zhí)行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //獲得viewController的生命周期方法的selector
        SEL systemSel = @selector(viewWillAppear:);
        //自己實(shí)現(xiàn)的將要被交換的方法的selector
        SEL swizzSel = @selector(swiz_viewWillAppear:);
        //兩個(gè)方法的Method
        Method systemMethod = class_getInstanceMethod([self class], systemSel);
        Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
        //首先動(dòng)態(tài)添加方法,實(shí)現(xiàn)是被交換的方法贡定,返回值表示添加成功還是失敗
        BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
        if (isAdd) {
            //如果成功赋访,說明類中不存在這個(gè)方法的實(shí)現(xiàn)
            //將被交換方法的實(shí)現(xiàn)替換到這個(gè)并不存在的實(shí)現(xiàn)
            class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
        }else{
            //否則,交換兩個(gè)方法的實(shí)現(xiàn)
            method_exchangeImplementations(systemMethod, swizzMethod);
        }
    });
}
- (void)swiz_viewWillAppear:(BOOL)animated{
    NSLog(@"11111111111");
}
- (void)viewWillAppear:(BOOL)animated{
    NSLog(@"22222222222");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self swiz_viewWillAppear:YES];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缓待,一起剝皮案震驚了整個(gè)濱河市蚓耽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旋炒,老刑警劉巖步悠,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瘫镇,居然都是意外死亡贤徒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門汇四,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踢涌,你說我怎么就攤上這事通孽。” “怎么了睁壁?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵背苦,是天一觀的道長互捌。 經(jīng)常有香客問我,道長行剂,這世上最難降的妖魔是什么秕噪? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮厚宰,結(jié)果婚禮上腌巾,老公的妹妹穿的比我還像新娘。我一直安慰自己铲觉,他們只是感情好澈蝙,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撵幽,像睡著了一般灯荧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盐杂,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天逗载,我揣著相機(jī)與錄音,去河邊找鬼链烈。 笑死厉斟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的测垛。 我是一名探鬼主播捏膨,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼食侮!你這毒婦竟也來了号涯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤锯七,失蹤者是張志新(化名)和其女友劉穎链快,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眉尸,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡域蜗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了噪猾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霉祸。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖袱蜡,靈堂內(nèi)的尸體忽然破棺而出丝蹭,到底是詐尸還是另有隱情,我是刑警寧澤坪蚁,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布奔穿,位于F島的核電站镜沽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贱田。R本人自食惡果不足惜缅茉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望男摧。 院中可真熱鬧蔬墩,春花似錦、人聲如沸彩倚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帆离。三九已至蔬蕊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哥谷,已是汗流浹背岸夯。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留们妥,地道東北人猜扮。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像监婶,于是被迫代替她去往敵國和親旅赢。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉惑惶,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,686評論 0 9
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語言煮盼,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,182評論 0 7
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,548評論 33 466
  • 魏陽是斜ㄆ疲花,其實(shí)長的也不算是多么的了不得千绪,就是那種甜甜的妹子型充易,挺可愛的。反正追她的男生特別多荸型。 大學(xué)畢業(yè)蔽氨,靠著自...
    kaka醬閱讀 151評論 0 0
  • 要能在一瞬之間解決處理用戶的某個(gè)點(diǎn),這樣才能產(chǎn)生分享的欲望。 用戶的交易分享往往是一個(gè)簡短的行為鹉究,一旦將這個(gè)行為時(shí)...
    王二道閱讀 247評論 0 1