Runtime 消息的實例

本文主要記錄了Runtime的消息轉(zhuǎn)發(fā)過程和我們可以利用這個消息轉(zhuǎn)發(fā)機制來做一下事情。

消息


object-c 的消息[receive selector],最終都會變成 objc_msgSend(receive,selector),objc_msgSend只負(fù)責(zé)給消息接受者發(fā)送消息,尋找我們需要調(diào)用的方法。

UIButton *btn = [UIButton new];
 [button setBackgroundColor:[UIColor redColor]];
相當(dāng)于
objc_msgSend(button,@selector(setBackgroundColor:), [UIColor redColor]);
消息轉(zhuǎn)發(fā).png
IMP

typedef id (*IMP)(id, SEL, ...); 本質(zhì)上就是一個C語言的函數(shù)指針,他只真正能找到我們要執(zhí)行的方法的地址。

Runtime 方法調(diào)用過程

1、判斷target是否為空,selector的方法是否需要執(zhí)行,是否可以執(zhí)行
2、在cache中方法,如果沒有找到
3、就會在方法列表中尋找,一直到NSObject的方法
4、如果找不到,就要進行動態(tài)方法解析
5、消息重定向

這里主要是介紹方法動態(tài)解析和消息轉(zhuǎn)發(fā),就對objc_class和objc_cache IMP 不介紹了。

Runtime 動態(tài)方法解析

+ (BOOL)resolveClassMethod:(SEL)aSel
+ (BOOL)resolveInstanceMethod:(SEL)aSel

#import <Foundation/Foundation.h>

@interface Foot : NSObject

- (void)resolveInstanMethod;

@end


#import "Foot.h"
#import "Objc/runtime.h"
@implementation Foot

void  footCanBeEat(id self, SEL _cmd)
{
    NSLog(@"foot can be eat");
}

+ (BOOL)resolveInstanceMethod:(SEL)aSel
{
    if (aSel == @selector(resolveInstanMethod)) {
        class_addMethod([self class], @selector(resolveInstanMethod), (IMP)footCanBeEat, "v@:");
    }
    return NO;
}


@end


調(diào)用過程
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    Foot *YY = [[Foot alloc]init];
    [YY resolveInstanMethod];
    
}

Foot 想外聲明了一個方法,resolveInstanMethod, 但是里面并沒有這個方法的實現(xiàn)。而是通過重寫 resolveInstanceMethod, 通過class_addMethod,來實現(xiàn)動態(tài)添加方法。

如果沒有重寫方法的動態(tài)解析,Runtime就會做方法的重定向,重定向就是將消息重新發(fā)送給另外一個對象。

重定向

如上面的例子,如果我們沒有實現(xiàn)方法的動態(tài)解析,我們可以講發(fā)送給我的消息,轉(zhuǎn)發(fā)給另外一個對象。

*例子

#import <Foundation/Foundation.h>

@interface Foot : NSObject
@end


void  footCanBeEat(id self, SEL _cmd)
{
    NSLog(@"foot can be eat");
}

+ (BOOL)resolveInstanceMethod:(SEL)aSel
{
    if (aSel == @selector(resolveInstanMethod)) {
        class_addMethod([self class], @selector(resolveInstanMethod), (IMP)footCanBeEat, "v@:");
    }
    return NO;
}

@end




#import <Foundation/Foundation.h>

@interface Person : NSObject

- (void)resolveInstanMethod;

@end


*********************************************
#import "Person.h"
#import "Foot.h"
#import "Objc/runtime.h"

@interface Person ()

@property (nonatomic, strong) Foot *myFoot;

@end

@implementation Person

- (instancetype)init
{
    self = [super init];
    if (self) {
        _myFoot = [[Foot alloc]init];
    }
    return self;
}

-(id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(resolveInstanMethod)) {
        return _myFoot;
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end

*****************************************************
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    Person *YY = [[Person alloc]init];
    [YY resolveInstanMethod];
    
}

從這里看見,person的事例YY 調(diào)用了resolveInstanMethod,但是他根本就沒有實現(xiàn)這個方法。也沒有做方法的動態(tài)解析,而是走了方法的重定向,將這個消息轉(zhuǎn)發(fā)給了Foot的實體myfoot,F(xiàn)oot也沒有聲明resolveInstanMethod,但是他動態(tài)解析了這個方法。

所以上面方法的最終實現(xiàn)這是Foot,消息最開始的target是Person這樣就實現(xiàn)了方法的重定向。

關(guān)聯(lián)屬性


關(guān)聯(lián)屬性,就是用一個關(guān)鍵字,給一個對象附加一個屬性。
方法:

 objc_setAssociatedObject()
 objc_getAssociatedObject()

例子1:
通過buttonPhoneKey,我們將phoneNUmber和button綁定到了一起,就相當(dāng)于給這個button增加了一個屬性。屬性關(guān)聯(lián)一般的使用場景就是在category給一個類增加一個屬性。

- (void)viewDidload{
    static const void *buttonPhoneKey = "phoneNum";
    NSString *phoneNumber = @"18126504453";
    
    UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
    [button setTitle:@"associate" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor greenColor] forState:UIControlStateNormal];
    [button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
    
    objc_setAssociatedObject(button, buttonPhoneKey, phoneNumber, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    [self.view addSubview:button];
}

- (void)buttonAction:(UIButton *)btn
{
   NSString *phoneNum = objc_getAssociatedObject(btn, buttonPhoneKey);
    NSLog(@"phone num = %@ ", phoneNum);
}

例子2:通過屬性關(guān)聯(lián),給UIButton 的even添加block,這樣Button的事件就可以很方便的使用了,特別是很多需要用tag來區(qū)分的事件。

#import <UIKit/UIKit.h>
typedef    void(^tapAction)(UIButton *sender) ;
@interface UIButton (Block)
- (void)controlEvent:(UIControlEvents )event withBlock:(tapAction)block;
@end


#import "UIButton+Block.h"
#import "objc/runtime.h"

static const void *buttonAssociateKey = &buttonAssociateKey;
@implementation UIButton (Block)

- (void)controlEvent:(UIControlEvents )event withBlock:(tapAction)block
{
    objc_setAssociatedObject(self, buttonAssociateKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
    [self addTarget:self action:@selector(buttonAction:) forControlEvents:event];
}

- (void)buttonAction:(UIButton *)btn
{
    tapAction block =  objc_getAssociatedObject(btn, buttonAssociateKey);
    if (block) {
        block(btn);
    }
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,049評論 0 9
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,248評論 0 9
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 832評論 0 2
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 886評論 0 1
  • Runtime是什么 Runtime 又叫運行時,是一套底層的 C 語言 API,其為 iOS 內(nèi)部的核心之一,我...
    SuAdrenine閱讀 979評論 0 3

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