runtime之Method Swizzing

首先: 非常感謝大神的文章以及網(wǎng)上各種各樣的學(xué)習(xí)資料,讓我學(xué)習(xí)到了很多的知識(shí),并且稍微運(yùn)用了一下.我的Demo
動(dòng)態(tài)添加方法, 交換類方法, 截取系統(tǒng)方法換成自定義方法, 使用runtime給系統(tǒng)類添加屬性,獲取所有成員變量, 獲取屬性列表, 獲取協(xié)議列表, 獲取方法列表這些在崢大神等人的文章(runtime中有鏈接)中都有介紹,我就不畫蛇添足了.
簡(jiǎn)單列舉一下我在工作中使用到的Method Swizzing吧(很多都是在網(wǎng)上和別人學(xué)習(xí)的,當(dāng)然也有自己弄的)
只是簡(jiǎn)單介紹一下, 然后就是上代碼的時(shí)候了

  1. 輸出各個(gè)控制器名稱(多人開發(fā)的時(shí)候控制器不太好尋找,我們?cè)谶M(jìn)入每個(gè)控制器的時(shí)候輸出一下控制器的名字就很好找了)
    .h
//
//  UIViewController+Statistical.h
//  MethodSwizzingConclusion(黑魔法總結(jié))
//
//  Created by 吳曉群 on 16/10/10.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//
/**
 *  輸出當(dāng)前控制器名稱   分類黑魔法不需要導(dǎo)入頭文件即可使用
 *
 *  @param Statistical 類別
 */
#import <UIKit/UIKit.h>
@interface UIViewController (Statistical)
@end

.m

//
//  UIViewController+Statistical.m
//  MethodSwizzingConclusion(黑魔法總結(jié))
//
//  Created by 吳曉群 on 16/10/10.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//
#import "UIViewController+Statistical.h"
#import <objc/runtime.h>
@implementation UIViewController (Statistical)
+ (void)load
{
    [super load];
    Method methodDid = class_getInstanceMethod([self class], @selector(viewDidLoad));
    Method methodStatistical = class_getInstanceMethod([self class], @selector(statisticalViewDidLoad));
    method_exchangeImplementations(methodDid, methodStatistical);
}

/**
 *  輸出控制器名稱
 */
- (void)statisticalViewDidLoad
{
    NSString *string = [NSString stringWithFormat:@"%@",self.class];
    //這里加一個(gè)判斷, 將系統(tǒng)的UIViewController的對(duì)象剔除掉
    if (![string containsString:@"UI"])
    {
        NSLog(@"當(dāng)前控制器名稱 : %@",self.class);
    }
    /**
     *  因?yàn)榉椒ㄒ呀?jīng)交換, 實(shí)際上這個(gè)方法調(diào)用的是[self viewDidLoad];
     */
    [self statisticalViewDidLoad];
}
@end

2.防止數(shù)組越界崩潰
.h

//
//  NSArray+Crash.h
//  MethodSwizzingConclusion(黑魔法總結(jié))
//
//  Created by 吳曉群 on 16/10/10.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSArray (Crash)

@end

.m

//
//  NSArray+Crash.m
//  MethodSwizzingConclusion(黑魔法總結(jié))
//
//  Created by 吳曉群 on 16/10/10.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//

#import "NSArray+Crash.h"
#import <objc/runtime.h>

@implementation NSArray (Crash)
+ (void)load
{
    [super load];
    
    //這里不能使用[self class],因?yàn)開_NSArrayI才是NSArray真正的類. 通過runtime函數(shù)獲取真正的類objc_getClass("__NSArrayI");
    //一些常用類簇真身
    //NSArray                   __NSArrayI
    //NSMutableAray             __NSArrayM
    //NSDictionary              __NSDictionaryI
    //NSMutableDictionary       __NSDictionaryM
    
    Method objcMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    Method crashMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(crashObjectAtIndex:));
    method_exchangeImplementations(objcMethod, crashMethod);
}

#pragma mark ----判斷數(shù)組是否越界,越界則輸出
- (id)crashObjectAtIndex:(NSUInteger)index
{
    if (index > self.count - 1)
    {
        /**
         *  做一下異常處理,   C++語(yǔ)法
         */
        @try {
            return [self crashObjectAtIndex:index];
        }
        @catch (NSException *exception) {
//            NSLog(@"----------  %s Crash Because Method %s  ----------\n",class_getName(self.class), __func__);
//            NSLog(@"%@",[exception callStackSymbols]);
            
            //或者這樣輸出也行
            NSLog(@"異常名稱: %@   異常原因: %@",exception.name,exception.reason);
            NSLog(@"%@",[exception callStackSymbols]);
        }
        @finally {}
        
        return nil;
    }
    else
    {
        return [self crashObjectAtIndex:index];
    }
}
@end

3.判斷系統(tǒng)版本使用圖片
.h

//
//  UIImage+ChangeImage.h
//  WXQRuntime
//
//  Created by 吳曉群 on 16/9/23.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UIImage (ChangeImage)
/**
 *  使用runtime給系統(tǒng)類添加屬性
 */
@property (nonatomic, copy)NSString *titleString;       //類別屬性
@end

.m

//
//  UIImage+ChangeImage.m
//  WXQRuntime
//
//  Created by 吳曉群 on 16/9/23.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//

#import "UIImage+ChangeImage.h"
#import <objc/runtime.h>            //runtime類

@implementation UIImage (ChangeImage)


#pragma mark ----給類別添加屬性
static const char *titleStringKey;

- (void)setTitleString:(NSString *)titleString
{
    /**
     *  第一個(gè)參數(shù): 給哪個(gè)對(duì)象添加關(guān)聯(lián)
     第二個(gè)參數(shù): 關(guān)聯(lián)的key, 通過這個(gè)key獲取
     第三個(gè)參數(shù): 關(guān)聯(lián)的value
     第四個(gè)參數(shù): 關(guān)聯(lián)的策略,就是copy, strong, assign 等
     */
    objc_setAssociatedObject(self, &titleStringKey, titleString, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)titleString
{
    //根據(jù)關(guān)聯(lián)的key,獲取關(guān)聯(lián)的值.
    return objc_getAssociatedObject(self, &titleStringKey);
}


#pragma mark ----自定義實(shí)現(xiàn)圖片方法
+ (UIImage *)WXQ_imageName:(NSString *)name
{
    double verSion = [UIDevice currentDevice].systemVersion.doubleValue;
    if ((verSion >= 7.0))
    {
        //如果系統(tǒng)版本是7.0以上, 使用另外一套文件名結(jié)尾是'_os7'的圖片
        name = [name stringByAppendingString:@"_os7"];
    }
    UIImage *image = [UIImage WXQ_imageName:name];
    if (image == nil)
    {
        NSLog(@"圖片沒有加載出來(lái)");
    }
    return [UIImage WXQ_imageName:name];
}

//加載分類到內(nèi)存的時(shí)候調(diào)用
+ (void)load
{
    /**
     *  通過class_getInstanceMethod()函數(shù)從當(dāng)前對(duì)象中的method list 獲取method結(jié)構(gòu)體,如果是類方法就使用class_getClassMethod()函數(shù)獲取
     *
     *  @param class]     類
     *  @param suiBianXie 對(duì)象方法(減號(hào)方法)
     *
     *  @return 返回哪個(gè)類中的減號(hào)方法
     */
//    Method methodObject = class_getInstanceMethod([self class], @selector(suiBianXie));
    
    //獲取兩個(gè)類的類方法
    Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
    Method m2 = class_getClassMethod([UIImage class], @selector(WXQ_imageName:));
    
    method_exchangeImplementations(m1, m2);
}
@end

4.防止按鈕多次點(diǎn)擊
.h

//
//  UIButton+PreVentMultipleClick.h
//  MethodSwizzingConclusion(黑魔法總結(jié))
//
//  Created by 吳曉群 on 16/10/11.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//
/**
 *  使用黑魔法防止按鈕在一定時(shí)間內(nèi)被多次點(diǎn)擊造成多次跳轉(zhuǎn)至同一個(gè)界面,但是這會(huì)造成無(wú)論點(diǎn)擊哪個(gè)按鈕, 在規(guī)定的時(shí)間內(nèi)都不能再點(diǎn)擊任何按鈕
 */
#import <UIKit/UIKit.h>

@interface UIButton (PreVentMultipleClick)

@property (nonatomic, assign)NSTimeInterval clickDurationTime;  //點(diǎn)擊間隔時(shí)間
@end

.m

//
//  UIButton+PreVentMultipleClick.m
//  MethodSwizzingConclusion(黑魔法總結(jié))
//
//  Created by 吳曉群 on 16/10/11.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//

#import "UIButton+PreVentMultipleClick.h"
#import <objc/runtime.h>

@implementation UIButton (PreVentMultipleClick)

//默認(rèn)的按鈕點(diǎn)擊時(shí)間
static const NSTimeInterval defaultDuration = 0.1;

//記錄是否忽略按鈕點(diǎn)擊事件, 默認(rèn)第一次執(zhí)行事件
static BOOL _isIgnoreEvent = NO;
//設(shè)置執(zhí)行按鈕事件狀態(tài)
static void resetState(){
    _isIgnoreEvent = NO;
}

static const char *clickDurationTimeKey = "clickDutationTimeKey";
#pragma mark ----關(guān)聯(lián)對(duì)象
- (void)setClickDurationTime:(NSTimeInterval)clickDurationTime
{
#warning 這里最后的類型不能使用OBJC_ASSOCIATION_ASSIGN,否則時(shí)間間隔不是整數(shù)的話會(huì)崩潰.寫成這樣就可以了
    objc_setAssociatedObject(self, clickDurationTimeKey, @(clickDurationTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSTimeInterval)clickDurationTime
{
    return [objc_getAssociatedObject(self, clickDurationTimeKey) floatValue];
}

#pragma mark ----重寫系統(tǒng)點(diǎn)擊方法
- (void)replaceSendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    // 保險(xiǎn)起見, 判斷下class類型
    if ([self isKindOfClass:[UIButton class]])
    {
        //1. 按鈕點(diǎn)擊間隔事件
        self.clickDurationTime = self.clickDurationTime == 0 ? defaultDuration : self.clickDurationTime;
        
        //2. 是否忽略按鈕點(diǎn)擊事件
        if (_isIgnoreEvent)
        {
            //2.1 忽略按鈕事件
            //直接攔截掉super函數(shù)進(jìn)行發(fā)送消息
            return;
        }
        else if (self.clickDurationTime > 0)
        {
            //2.2 不忽略按鈕事件
            //后續(xù)在間隔時(shí)間內(nèi)直接忽視按鈕事件
            [self replaceSendAction:action to:target forEvent:event];
            _isIgnoreEvent = YES;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.clickDurationTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                resetState();
            });
        }
    }
}

#pragma mark ----交換系統(tǒng)方法
+ (void)load
{
    Method buttonMethod = class_getInstanceMethod([self class], @selector(sendAction:to:forEvent:));
    Method replaceMethod = class_getInstanceMethod([self class], @selector(replaceSendAction:to:forEvent:));
    method_exchangeImplementations(buttonMethod, replaceMethod);
}
@end

5.去除字符串中的null(這里是因?yàn)槎嗳碎_發(fā), 別人接收請(qǐng)求數(shù)據(jù)的時(shí)候沒有做任何處理,導(dǎo)致項(xiàng)目中有一些地方會(huì)出現(xiàn)(null)字符,一開始打算在使用runtime在源頭(也就是請(qǐng)求數(shù)據(jù)的地方)進(jìn)行處理,但是后來(lái)感覺在終點(diǎn)(使用數(shù)據(jù)給界面賦值的時(shí)候)處理會(huì)比較方便一些)
.h

//
//  UILabel+RemoveNull.h
//  取出字符串中的null
//
//  Created by 吳曉群 on 16/10/18.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UILabel (RemoveNull)

@end

.m

//
//  UILabel+RemoveNull.m
//  取出字符串中的null
//
//  Created by 吳曉群 on 16/10/18.
//  Copyright ? 2016年 sanMiTeconology. All rights reserved.
//

#import "UILabel+RemoveNull.h"
#import <objc/runtime.h>

@implementation UILabel (RemoveNull)
+ (void)load
{
    [super load];
    Method method = class_getInstanceMethod([self class], @selector(setText:));
    Method removeMethod = class_getInstanceMethod([self class], @selector(removeNullSetText:));
    method_exchangeImplementations(method, removeMethod);
}

- (void)removeNullSetText:(NSString *)string
{
    if (string == nil || [string isEqualToString:@"(null)"])
    {
        string = @"";
    }
#pragma mark ----字符串中包含某個(gè)字符串
    else if ([string rangeOfString:@"(null)"].location != NSNotFound)
    {
#pragma mark ----使用某字符串代替原來(lái)的字符串
        string = [string stringByReplacingOccurrencesOfString:@"(null)" withString:@""];
    }
    [self removeNullSetText:string];
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浸颓,一起剝皮案震驚了整個(gè)濱河市空猜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖整份,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拙寡,死亡現(xiàn)場(chǎng)離奇詭異刽脖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)粒没,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)簇爆,“玉大人癞松,你說我怎么就攤上這事∪肭” “怎么了响蓉?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)哨毁。 經(jīng)常有香客問我枫甲,道長(zhǎng),這世上最難降的妖魔是什么扼褪? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任想幻,我火速辦了婚禮,結(jié)果婚禮上话浇,老公的妹妹穿的比我還像新娘脏毯。我一直安慰自己,他們只是感情好幔崖,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布食店。 她就那樣靜靜地躺著,像睡著了一般赏寇。 火紅的嫁衣襯著肌膚如雪吉嫩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天嗅定,我揣著相機(jī)與錄音率挣,去河邊找鬼。 笑死露戒,一個(gè)胖子當(dāng)著我的面吹牛椒功,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播智什,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼动漾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了荠锭?” 一聲冷哼從身側(cè)響起旱眯,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后删豺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體共虑,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年呀页,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妈拌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蓬蝶,死狀恐怖尘分,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丸氛,我是刑警寧澤培愁,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站缓窜,受9級(jí)特大地震影響定续,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜禾锤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一香罐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧时肿,春花似錦庇茫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至寸宏,卻和暖如春宁炫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氮凝。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工羔巢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罩阵。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓竿秆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親稿壁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幽钢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,732評(píng)論 0 9
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理傅是,服務(wù)發(fā)現(xiàn)匪燕,斷路器蕾羊,智...
    卡卡羅2017閱讀 134,716評(píng)論 18 139
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件帽驯、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,124評(píng)論 4 61
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,569評(píng)論 33 466
  • 過不了幾天就有人提醒你尼变,曾經(jīng)的你利凑,內(nèi)心有愛,無(wú)比善良享甸!正是這種善良截碴,讓人覺得你軟弱可欺梳侨!總會(huì)有人利用你的愛蛉威,用超乎...
    桉桉歐尼閱讀 776評(píng)論 16 5