最近看了一下runtime運(yùn)行時(shí)方面的文章,總結(jié)一下,加上自己的一下理解,如果文章有未全和不足的地方,歡迎各位在下方留言補(bǔ)充和指正。

目錄:
- runtime 消息機(jī)制
- runtime 添加屬性
- runtime 交換方法
- runtime 動(dòng)態(tài)添加方法
- runtime Class的常見(jiàn)方法
1.獲取成員變量列表
2.獲取屬性列表
3.獲取方法列表
4.獲取協(xié)議列表
5.獲得類(lèi)方法
6.獲得實(shí)例方法
7.添加方法
8.替換原方法實(shí)現(xiàn)
9.交換兩個(gè)方法
- runtime method swizzling 黑魔法
runtime 消息機(jī)制
對(duì)于OC代碼,調(diào)用方法的實(shí)質(zhì)就是一個(gè)消息發(fā)送,OC底層通過(guò)runtime實(shí)現(xiàn)
消息機(jī)制原理:對(duì)象根據(jù)方法編號(hào)SEL去映射表查找對(duì)應(yīng)的方法實(shí)現(xiàn)。
每一個(gè) OC 的方法,底層必然有一個(gè)與之對(duì)應(yīng)的 runtime 方法。
首先必須要導(dǎo)入頭文件 #import <objc/message.h>
-
在使用objc_msgSend方法編譯時(shí)可能出現(xiàn)報(bào)錯(cuò)的情況,對(duì)應(yīng)的解決辦法如下image.png
新建一個(gè)Person類(lèi) Person.h中代碼
/** 實(shí)例方法*/
// 無(wú)參數(shù)無(wú)返回值
- (void)run1;
// 無(wú)參數(shù)有返回值
- (NSString *)run2;
// 有參數(shù)無(wú)返回值
- (void)run3:(NSString *)string;
// 有參數(shù)有返回值
- (NSString *)run4:(NSString *)string;
/** 類(lèi)方法*/
// 無(wú)參數(shù)無(wú)返回值
+ (void)run1;
// 無(wú)參數(shù)有返回值
+ (NSString *)run2;
// 有參數(shù)無(wú)返回值
+ (void)run3:(NSString *)string;
// 有參數(shù)有返回值
+ (NSString *)run4:(NSString *)string;
- Person.m中代碼
- (void)run1 {
NSLog(@"實(shí)例方法 :run1");
}
- (NSString *)run2 {
return @"實(shí)例方法 :run2";
}
- (void)run3:(NSString *)string {
NSLog(@"run3 --- 參數(shù):%@", string);
}
- (NSString *)run4:(NSString *)string {
return [NSString stringWithFormat:@"run4 --- 參數(shù):%@", string];
}
+ (void)run1 {
NSLog(@"run1 classMethod");
}
+ (NSString *)run2 {
return @"run2 classMethod";
}
+ (void)run3:(NSString *)string {
NSLog(@"run3 classMethod --- 參數(shù):%@", string);
}
+ (NSString *)run4:(NSString *)string {
return [NSString stringWithFormat:@"run4 classMethod --- 參數(shù):%@", string];
}
- 利用objc_msgSend調(diào)用上面的這些方法
/** 對(duì)象方法/實(shí)例方法 */
// 底層的實(shí)際寫(xiě)法
Person *person = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
person = objc_msgSend(person, sel_registerName("init"));
NSLog(@"無(wú)參數(shù)無(wú)返回值:");
((void (*) (id, SEL)) (void *)objc_msgSend)(person, sel_registerName("run1"));
objc_msgSend(person, @selector(run1));
NSLog(@"無(wú)參數(shù)有返回值:");
NSString *run2Return1 = ((NSString *(*) (id, SEL)) (void *)objc_msgSend)(person, sel_registerName("run2"));
NSLog(@"%@", run2Return1);
NSString *run2Return2 = objc_msgSend(person, @selector(run2));
NSLog(@"%@", run2Return2);
NSLog(@"有參數(shù)無(wú)返回值:");
((void (*) (id, SEL, NSString *)) (void *)objc_msgSend)(person, sel_registerName("run3:"), @"3333");
NSLog(@"有參數(shù)有反回值:");
NSString *run4Return1 = ((NSString *(*) (id, SEL, NSString *)) (void *)objc_msgSend)(person, sel_registerName("run4:"), @"4444");
NSLog(@"%@", run4Return1);
NSLog(@"--------------------------");
/** 類(lèi)方法 */
NSLog(@"classMethod - 無(wú)參數(shù)無(wú)返回值:");
((void (*) (id, SEL)) (void *)objc_msgSend)(Person.class, sel_registerName("run1"));
objc_msgSend(Person.class, @selector(run1));
NSLog(@"classMethod - 無(wú)參數(shù)有返回值:");
NSString *run2Return1Class = ((NSString *(*) (id, SEL)) (void *)objc_msgSend)(Person.class, sel_registerName("run2"));
NSLog(@"%@", run2Return1Class);
NSString *run2Return2Class = objc_msgSend(Person.class, @selector(run2));
NSLog(@"%@", run2Return2Class);
NSLog(@"classMethod - 有參數(shù)無(wú)返回值:");
((void (*) (id, SEL, NSString *)) (void *)objc_msgSend)(Person.class, sel_registerName("run3:"), @"3333");
NSLog(@"classMethod - 有參數(shù)有反回值:");
NSString *run4Return1Class = ((NSString *(*) (id, SEL, NSString *)) (void *)objc_msgSend)(Person.class, sel_registerName("run4:"), @"4444");
NSLog(@"%@", run4Return1Class);
- 注:
/**
錯(cuò)誤寫(xiě)法(arm64崩潰偶爾發(fā)生)
*/
objc_msgSend(person, sel_registerName("run3:"), @"12345678");
/**
標(biāo)準(zhǔn)寫(xiě)法
*/
((void (*) (id, SEL, NSString *)) (void *)objc_msgSend)(person, sel_registerName("run3:"), @"不能直接寫(xiě)objc_msgSend,會(huì)出現(xiàn)崩潰的現(xiàn)象(正常應(yīng)該是可以的)");
- 解釋一下標(biāo)準(zhǔn)寫(xiě)法前面參數(shù)
((void (*) (id, SEL)) (void *)objc_msgSend)
1、第一個(gè)void代表是否有返回值
如果返回值是NSString類(lèi)型的 例:無(wú)參數(shù)有返回值的方法
寫(xiě)法:((NSString *(*) (id, SEL)) (void *)objc_msgSend)
2、(id, SEL)
如果方法有參數(shù),參數(shù)類(lèi)型是NSString 例:有參數(shù)無(wú)返回值
寫(xiě)法:((void *(*) (id, SEL, NSString *)) (void *)objc_msgSend)
3、關(guān)于離objc_msgSend最近的void從互聯(lián)網(wǎng)上還沒(méi)找到具體含義,還望知道的好友留言或私信告知
應(yīng)用與注意
注:使用objc_msgSend()創(chuàng)建對(duì)象不能自動(dòng)釋放,對(duì)象需要手動(dòng)release。使用runtime執(zhí)行初始化方法創(chuàng)建的對(duì)象的時(shí)候是不在ARC控制之下的,所以在該類(lèi)銷(xiāo)毀的時(shí)候需要手動(dòng)release
應(yīng)用:使用objc_msgSend()創(chuàng)建對(duì)象時(shí)好處,應(yīng)用之一就是在一個(gè)控制器要跳轉(zhuǎn)多個(gè)控制器的時(shí)候,不再需要每個(gè)控制器都單獨(dú)寫(xiě)一遍初始化,也不再需要每一個(gè)控制器單獨(dú)寫(xiě)release方法,使用runtime的話,使用一個(gè)或者根據(jù)情況使用幾個(gè)回調(diào),返回控制器的類(lèi)名以及相應(yīng)的參數(shù)就好了。
Class class = objc_getClass(controllerName.UTF8String); //或者 NSStringFromClass(<#Class _Nonnull __unsafe_unretained aClass#>)
id viewController = ((id(*)(id,SEL))objc_msgSend)(class,NSSelectorFromString(@"new"));
[self xpz_pushViewController:viewController];
// 導(dǎo)航控制器獲得控制權(quán)后進(jìn)行release即可
- (void)xpz_pushViewController:(__kindof UIViewController *)viewController
{
[self pushViewController:viewController];
//release
((void(*)(id,SEL))objc_msgSend)(viewController,NSSelectorFromString(@"release"));
}
category添加屬性
1.面試中經(jīng)常會(huì)被問(wèn)到如何給category添加屬性,在平時(shí)我們偶爾也會(huì)遇到想要在分類(lèi)中添加屬性的情況,雖然我們用了@property,但是僅僅會(huì)自動(dòng)生成get和set方法的聲明,并沒(méi)有帶下劃線的屬性和方法實(shí)現(xiàn)生成,其實(shí)可以使用runtime動(dòng)態(tài)添加屬性方法
2.還有另外一種方法: use @dynamic or provide a method implementation in this category
1.使用runtime動(dòng)態(tài)添加屬性
// objc_setAssociatedObject(將某個(gè)值跟某個(gè)對(duì)象關(guān)聯(lián)起來(lái),將某個(gè)值存儲(chǔ)到某個(gè)對(duì)象中)
// object:給哪個(gè)對(duì)象添加屬性
// key:屬性名稱(chēng)
// value:屬性值
// policy:保存策略
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
- 需求:在UIView上添加一個(gè)播放器
- objc_getAssociatedObject有兩個(gè)參數(shù),第一個(gè)參數(shù)為從該object中獲取關(guān)聯(lián)對(duì)象,第二個(gè)參數(shù)為想要獲取關(guān)聯(lián)對(duì)象的key;
對(duì)于第二個(gè)參數(shù)const void *key,有以下四種推薦的key值:
- 聲明
static char kAssociatedObjectKey;,使用&kAssociatedObjectKey作為 key 值; - 聲明
static void *kAssociatedObjectKey = &kAssociatedObjectKey;,使用kAssociatedObjectKey作為key值; - 用
selector,使用 getter 方法的名稱(chēng)作為key值; - 而使用
_cmd可以直接使用該@selector的名稱(chēng),即hideButton,并且能保證改名稱(chēng)不重復(fù)。(與上一種方法相同)
static char playerViewKey; // playerView
static void *playerLayerKey = &playerLayerKey; // playerLayer
/************************************************* playerView ********************************************************/
// getter
- (UIView *)playerView {
UIView *_playerView = objc_getAssociatedObject(self, &playerViewKey);
if (!_playerView) {
objc_setAssociatedObject(self, &playerViewKey, _playerView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return _playerView;
}
// setter
- (void)setPlayerView:(UIView *)playerView {
return objc_setAssociatedObject(self, &playerViewKey, playerView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
/************************************************* avplayer ********************************************************/
//getter
- (AVPlayer *)avPlayer {
AVPlayer *_avPlayer = objc_getAssociatedObject(self, @selector(avPlayer));
if (!_avPlayer) {
objc_setAssociatedObject(self, @selector(avPlayer), _avPlayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return _avPlayer;
}
//setter
- (void)setAvPlayer:(AVPlayer *)avPlayer {
objc_setAssociatedObject(self, @selector(avPlayer), avPlayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
/************************************************* playerLayer ********************************************************/
- (AVPlayerLayer *)playerLayer {
return objc_getAssociatedObject(self, playerLayerKey);
}
- (void)setPlayerLayer:(AVPlayerLayer *)playerLayer {
objc_setAssociatedObject(self, playerLayerKey, playerLayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
/************************************************* playerItem ********************************************************/
/**
getter方法
*/
- (AVPlayerItem *)playerItem {
return objc_getAssociatedObject(self, _cmd);
}
/**
setter方法
*/
- (void)setPlayerItem:(AVPlayerItem *)playerItem {
objc_setAssociatedObject(self, @selector(playerItem), playerItem, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
擴(kuò)展:
objc_getAssociatedObject與objc_setAssociatedObject方法另一種用途,在tableViewCell中的btn點(diǎn)擊事件中使用
在- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;方法中使用:
if (self.GroupListArr.count > 0) {
cell.hidden = NO;
CB_ActivityGroupModel *model = self.GroupListArr[0];
[cell setModel:model];
[cell.joinGroupBtn addTarget:self action:@selector(joinGroupAction:) forControlEvents:(UIControlEventTouchUpInside)];
objc_setAssociatedObject(cell.joinGroupBtn, &joinGroup, model, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
使用
- (void)joinGroupAction:(UIButton *)btn
{
CB_ActivityGroupModel *model = objc_getAssociatedObject(btn, &joinGroup);
CB_ActivityGroupDetailsVC *vc = [[CB_ActivityGroupDetailsVC alloc]init];
vc.goodModel = self.detailsModel;
vc.model = model;
[self.navigationController pushViewController:vc animated:YES];
}
runtime 交換方法
當(dāng)?shù)谌娇蚣?或者 系統(tǒng)原生方法功能不能滿(mǎn)足我們的時(shí)候,我們可以在保持系統(tǒng)原有方法功能的基礎(chǔ)上,添加額外的功能。
class_getInstanceMethod 得到類(lèi)的實(shí)例方法
class_getClassMethod 得到類(lèi)的類(lèi)方法
- 給系統(tǒng)的imageNamed添加額外功能
#import "UIImage+Image.h"
#import <objc/runtime.h>
@implementation UIImage (Image)
+ (void)load
{
Class class = [self class];
// 類(lèi)方法
// 1.獲取 imageNamed方法地址
Method originalMethod = class_getClassMethod(class, sel_registerName("imageNamed:"));
// 2.獲取 xpz_imageNamed方法地址
Method swizzledMethod = class_getClassMethod(class, @selector(xpz_imageNamed:));
// 交換 imageNamed:
method_exchangeImplementations(originalMethod, swizzledMethod);
}
/**
看清楚下面是不會(huì)有死循環(huán)的
調(diào)用 imageNamed => xpz_imageNamed
調(diào)用 xpz_imageNamed => imageNamed
*/
+ (nullable UIImage *)xpz_imageNamed:(NSString *)name{
UIImage *xpz_image = [UIImage xpz_imageNamed:name];
if (xpz_image) {
NSLog(@"runtime添加額外功能--加載成功");
} else {
NSLog(@"runtime添加額外功能--加載失敗");
}
return xpz_image;
}
@end
/**
不能在分類(lèi)中重寫(xiě)系統(tǒng)方法imageNamed,因?yàn)闀?huì)把系統(tǒng)的功能給覆蓋掉,而且分類(lèi)中不能調(diào)用super
所以第二步,我們要 自己實(shí)現(xiàn)一個(gè)帶有擴(kuò)展功能的方法.
+ (UIImage *)imageNamed:(NSString *)name {
}
*/
- 給UIViewController的viewWillAppear添加額外功能,就可以再控制臺(tái)看到每次控制器的變化
#import "UIViewController+hook.h"
#import <objc/runtime.h>
@implementation UIViewController (hook)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(hook_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)hook_viewWillAppear:(BOOL)animated {
[self hook_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
打印
[控制器:UIViewController+hook.m -- line: 53行] viewWillAppear: <NavigationViewController: 0x1060bfc00>
[控制器:UIViewController+hook.m -- line: 53行] viewWillAppear: <GroupViewController: 0x105a6c960>
[控制器:UIViewController+hook.m -- line: 53行] viewWillAppear: <NavigationViewController: 0x10607e400>
- 為什么要添加didAddMethod判斷?
先嘗試添加原SEL其實(shí)是為了做一層保護(hù),因?yàn)槿绻@個(gè)類(lèi)沒(méi)有實(shí)現(xiàn)originalSelector,但其父類(lèi)實(shí)現(xiàn)了,那class_getInstanceMethod會(huì)返回父類(lèi)的方法。這樣method_exchangeImplementations替換的是父類(lèi)的那個(gè)方法,這當(dāng)然不是我們想要的。所以我們先嘗試添加 orginalSelector,如果已經(jīng)存在,再用 method_exchangeImplementations 把原方法的實(shí)現(xiàn)跟新的方法實(shí)現(xiàn)給交換掉。
大概的意思就是我們可以通過(guò)class_addMethod為一個(gè)類(lèi)添加方法(包括方法名稱(chēng)(SEL)和方法的實(shí)現(xiàn)(IMP)),返回值為BOOL類(lèi)型,表示方法是否成功添加。需要注意的地方是class_addMethod會(huì)添加一個(gè)覆蓋父類(lèi)的實(shí)現(xiàn),但不會(huì)取代原有類(lèi)的實(shí)現(xiàn)。也就是說(shuō)如果class_addMethod返回YES,說(shuō)明子類(lèi)中沒(méi)有方法originalSelector,通過(guò)class_addMethod為其添加了方法originalSelector,并使其實(shí)現(xiàn)(IMP)為我們想要替換的實(shí)現(xiàn)。
runtime動(dòng)態(tài)地添加方法
float runtime_addMethod(id receiver, SEL sel, const void *arg1, int arg2)
{
NSLog(@"方法名:%s, 參數(shù)1:%@, 參數(shù)2:%d", __FUNCTION__, [NSString stringWithUTF8String:arg1], arg2);
return 1;
}
Person_runtimeVC *vc = objc_msgSend(objc_getClass("Person_runtimeVC"), sel_registerName("alloc"));
vc = objc_msgSend(vc, sel_registerName("init"));
/** 添加方法*/
// 動(dòng)態(tài)添加run方法
// class: 給哪個(gè)類(lèi)添加方法
// SEL: 添加哪個(gè)方法,即添加方法的方法編號(hào)
// IMP: 方法實(shí)現(xiàn) => 函數(shù) => 函數(shù)入口 => 函數(shù)名(添加方法的函數(shù)實(shí)現(xiàn)(函數(shù)地址))
// type: 方法類(lèi)型,(返回值+參數(shù)類(lèi)型) v:void @:對(duì)象->self :表示SEL->_cmd
class_addMethod([vc class], NSSelectorFromString(@"runtime_addMethod"), (IMP)runtime_addMethod, "f@:r^vd");
int returnValue = ((float (*)(id, SEL, const void *, int))objc_msgSend)((id)vc, NSSelectorFromString(@"runtime_addMethod"), "參數(shù)1", 10086);
NSLog(@"返回值:%d", returnValue);
NSLog(@"%s", @encode(const void *));
/**打印結(jié)果*/
[控制器:ViewController.m -- line: 108行] 方法名:runtime_addMethod, 參數(shù)1:參數(shù)1, 參數(shù)2:10086
[控制器:ViewController.m -- line: 74行] 返回值:1
[控制器:ViewController.m -- line: 75行] r^v
- 參數(shù):
"f@:r^vd"
第1個(gè)字符:表示函數(shù)(方法)返回值類(lèi)型,這里返回值類(lèi)型是 `float` ,故為 `f`
第2、3個(gè)字符:蘋(píng)果解釋是由于函數(shù)(方法)至少帶有兩個(gè)參數(shù)(self和_cmd)還記得之前的 (id,SEL) 么,所以第2、3個(gè)字符必須是 ‘@:’,其實(shí)我們當(dāng)做固定寫(xiě)法就好了
第4個(gè)字符根據(jù)NSLog(@"%s", @encode(const void *));打印結(jié)果可以看出來(lái)r^v是第三個(gè)參數(shù)的類(lèi)型,第四個(gè)參數(shù)是int,所以是d
runtime 常見(jiàn)方法
原著http://www.itdecent.cn/p/46dd81402f63
- 獲取成員變量
/** 獲取類(lèi)中的所有成員變量*/
Ivar *ivarList = class_copyIvarList([Person_runtimeVC class], &count);
for(int i = 0; i < count; i++) {
// 根據(jù)角標(biāo),從數(shù)組取出對(duì)應(yīng)的成員變量
Ivar ivar = ivarList[i];
// 獲取成員變量名字
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 處理成員變量名->字典中的key(去掉 _ ,從第一個(gè)角標(biāo)開(kāi)始截取)
NSString *key = [ivarName substringFromIndex:1];
NSLog(@"獲取類(lèi)成員變量:%@ --- %@",ivarName,key);
}
- 獲取屬性列表
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]);
}
- 獲取方法列表
Method *methodList = class_copyMethodList([self class], &count);
for (unsigned int i; i<count; i++) {
Method method = methodList[i];
NSLog(@"method---->%@", NSStringFromSelector(method_getName(method)));
}
- 獲取協(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]);
}
現(xiàn)在有一個(gè)Person類(lèi),和person創(chuàng)建的xiaoming對(duì)象,有test1和test2兩個(gè)方法
- 獲得類(lèi)方法
Class PersonClass = object_getClass([Person class]);
SEL oriSEL = @selector(test1);
Method oriMethod = class_getInstanceMethod(xiaomingClass, oriSEL);
- 獲得實(shí)例方法
Class PersonClass = object_getClass([xiaoming class]);
SEL oriSEL = @selector(test2);
Method cusMethod = class_getInstanceMethod(xiaomingClass, oriSEL);
- 添加方法
BOOL addSucc = class_addMethod(xiaomingClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
- 替換原方法實(shí)現(xiàn)
class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
- 交換兩個(gè)方法
method_exchangeImplementations(oriMethod, cusMethod);
什么是 method swizzling(俗稱(chēng)黑魔法)
- 簡(jiǎn)單說(shuō)就是進(jìn)行方法交換
- 在Objective-C中調(diào)用一個(gè)方法,其實(shí)是向一個(gè)對(duì)象發(fā)送消息,查找消息的唯一依據(jù)是selector的名字。利用Objective-C的動(dòng)態(tài)特性,可以實(shí)現(xiàn)在運(yùn)行時(shí)偷換selector對(duì)應(yīng)的方法實(shí)現(xiàn),達(dá)到給方法掛鉤的目的
-
每個(gè)類(lèi)都有一個(gè)方法列表,存放著方法的名字和方法實(shí)現(xiàn)的映射關(guān)系,selector的本質(zhì)其實(shí)就是方法名,IMP有點(diǎn)類(lèi)似函數(shù)指針,指向具體的Method實(shí)現(xiàn),通過(guò)selector就可以找到對(duì)應(yīng)的IMP
image
selector --> 對(duì)應(yīng)的IMP
交換方法的幾種實(shí)現(xiàn)方式
利用 method_exchangeImplementations 交換兩個(gè)方法的實(shí)現(xiàn)
利用 class_replaceMethod 替換方法的實(shí)現(xiàn)
-
利用 method_setImplementation 來(lái)直接設(shè)置某個(gè)方法的IMP。
image
這里可以參考簡(jiǎn)友這篇:【Runtime Method Swizzling開(kāi)發(fā)實(shí)例匯總】http://www.itdecent.cn/p/f6dad8e1b848
runtime知識(shí)很多,后期會(huì)慢慢補(bǔ)充,歡迎廣大簡(jiǎn)友補(bǔ)充學(xué)習(xí);剛學(xué)習(xí)使用Markdown,排版上做的不是太好。
本篇筆記部分參考自以下:


