首先: 非常感謝大神的文章以及網(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í)候了
- 輸出各個(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