首先來看一道面試題。 下列代碼中Person
繼承自NSObject
,Student
繼承自Person
,寫出下列代碼輸出內容。c++
#import "Student.h"
@implementation Student
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]);
NSLog(@"[self superclass] = %@", [self superclass]);
NSLog(@"----------------");
NSLog(@"[super class] = %@", [super class]);
NSLog(@"[super superclass] = %@", [super superclass]);
}
return self;
}
@end
複製代碼
直接來看一下打印內容面試
Runtime-super[6601:1536402] [self class] = Student
Runtime-super[6601:1536402] [self superclass] = Person
Runtime-super[6601:1536402] ----------------
Runtime-super[6601:1536402] [super class] = Student
Runtime-super[6601:1536402] [super superclass] = Person
複製代碼
上述代碼中能夠發現不管是self
仍是super
調用class
或superclass
的結果都是相同的。bash
爲何結果是相同的?super
關鍵字在調用方法的時候底層調用流程是怎樣的?函數
咱們經過一段代碼來看一下super
底層實現,爲Person
類提供run
方法,Student
類中重寫run
方法,方法內部調用[super run];
,將Student.m
轉化爲c++
代碼查看其底層實現。源碼分析
- (void) run
{
[super run];
NSLog(@"Student...");
}
複製代碼
上述代碼轉化爲c++代碼post
static void _I_Student_run(Student * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("run"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jm_dztwxsdn7bvbz__xj2vlp8980000gn_T_Student_e677aa_mi_0);
}
複製代碼
經過上述源碼能夠發現,[super run];
轉化爲底層源碼內部其實調用的是objc_msgSendSuper
函數。學習
objc_msgSendSuper
函數內傳遞了兩個參數。__rw_objc_super
結構體和sel_registerName("run")
方法名。ui
__rw_objc_super
結構體內傳入的參數是self
和class_getSuperclass(objc_getClass("Student"))
也就是Student
的父類Person
編碼
首先咱們找到objc_msgSendSuper
函數查看內部結構atom
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
複製代碼
能夠發現objc_msgSendSuper
中傳入的結構體是objc_super
,咱們來到objc_super
內部查看其內部結構。 咱們經過源碼查找objc_super
結構體查看其內部結構。
// 精簡後的objc_super結構體
struct objc_super {
__unsafe_unretained _Nonnull id receiver; // 消息接受者
__unsafe_unretained _Nonnull Class super_class; // 消息接受者的父類
/* super_class is the first class to search */
// 父類是第一個開始查找的類
};
複製代碼
從objc_super
結構體中能夠發現receiver
消息接受者仍然爲self
,superclass
僅僅是用來告知消息查找從哪個類開始。從父類的類對象開始去查找。
咱們經過一張圖看一下其中的區別。
從上圖中咱們知道 super
調用方法的消息接受者receiver
仍然是self
,只是從父類的類對象開始去查找方法。
那麼此時從新回到面試題,咱們知道class的底層實現以下面代碼所示
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
複製代碼
class
內部實現是根據消息接受者返回其對應的類對象,最終會找到基類的方法列表中,而self
和super
的區別僅僅是self
從本類類對象開始查找方法,super
從父類類對象開始查找方法,所以最終獲得的結果都是相同的。
另外咱們在回到run
方法內部,很明顯能夠發現,若是super
不是從父類開始查找方法,從本類查找方法的話,就調用方法自己形成循環調用方法而crash。
同理superclass
底層實現同class
相似,其底層實現代碼以下入所示
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
複製代碼
所以獲得的結果也是相同的。
上述OC代碼轉化爲c++代碼並不能說明super
底層調用函數就必定objc_msgSendSuper
。
其實super
底層真正調用的函數時objc_msgSendSuper2函數
咱們能夠經過查看super調用方法轉化爲彙編代碼來驗證這一說法
- (void)viewDidLoad {
[super viewDidLoad];
}
複製代碼
經過斷點查看其彙編調用棧
上圖中能夠發現super
底層其實調用的是objc_msgSendSuper2
函數,咱們來到源碼中查找一下objc_msgSendSuper2
函數的底層實現,咱們能夠在彙編文件中找到其相關底層實現。
ENTRY _objc_msgSendSuper2
UNWIND _objc_msgSendSuper2, NoFrame
MESSENGER_START
ldp x0, x16, [x0] // x0 = real receiver, x16 = class
ldr x16, [x16, #SUPERCLASS] // x16 = class->superclass
CacheLookup NORMAL
END_ENTRY _objc_msgSendSuper2
複製代碼
經過上面彙編代碼咱們能夠發現,其實底層是在函數內部調用的class->superclass
獲取父類,並非咱們上面分析的直接傳入的就是父類對象。
其實_objc_msgSendSuper2
內傳入的結構體爲objc_super2
struct objc_super2 {
id receiver;
Class current_class;
};
複製代碼
咱們能夠發現objc_super2
中除了消息接受者receiver
,另外一個成員變量current_class
也就是當前類對象。
與咱們上面分析的不一樣_objc_msgSendSuper2
函數內其實傳入的是當前類對象,而後在函數內部獲取當前類對象的父類,而且從父類開始查找方法。
咱們也能夠經過代碼驗證上述結構體內成員變量到底是當前類對象仍是父類對象。下文中咱們會經過另一道面試題驗證。
首先看一下isKindOfClass isKindOfClass
對象方法底層實現
- (BOOL)isMemberOfClass:(Class)cls {
// 直接獲取實例類對象並判斷是否等於傳入的類對象
return [self class] == cls;
}
- (BOOL)isKindOfClass:(Class)cls {
// 向上查詢,若是找到父類對象等於傳入的類對象則返回YES
// 直到基類還不相等則返回NO
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
複製代碼
isKindOfClass isKindOfClass
類方法底層實現
// 判斷元類對象是否等於傳入的元類元類對象
// 此時self是類對象 object_getClass((id)self)就是元類
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
// 向上查找,判斷元類對象是否等於傳入的元類對象
// 若是找到基類還不相等則返回NO
// 注意:這裏會找到基類
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
複製代碼
經過上述源碼分析咱們能夠知道。 isMemberOfClass
判斷左邊是否恰好等於右邊類型。 isKindOfClass
判斷左邊或者左邊類型的父類是否恰好等於右邊類型。 注意:類方法內部是獲取其元類對象進行比較
咱們查看如下代碼
NSLog(@"%d",[Person isKindOfClass: [Person class]]);
NSLog(@"%d",[Person isKindOfClass: object_getClass([Person class])]);
NSLog(@"%d",[Person isKindOfClass: [NSObject class]]);
// 輸出內容
Runtime-super[46993:5195901] 0
Runtime-super[46993:5195901] 1
Runtime-super[46993:5195901] 1
複製代碼
分析上述輸出內容: 第一個 0:上面提到過類方法是獲取self的元類對象與傳入的參數進行比較,可是第一行咱們傳入的是類對象,所以返回NO。
第二個 1:同上,此時咱們傳入Person元類對象,此時返回YES。驗證上述說法
第三個 1:咱們發現此時傳入的是NSObject類對象並非元類對象,可是返回的值倒是YES。 緣由是基元類的superclass指針是指向基類對象的。以下圖13號線
那麼Person元類
經過superclass
指針一直找到基元類,仍是不相等,此時再次經過superclass
指針來到基類,那麼此時發現相等就會返回YES了。
經過一道面試題對以前學習的知識進行復習。 問:如下代碼是否能夠執行成功,若是能夠,打印結果是什麼。
// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
- (void)test;
@end
// Person.m
#import "Person.h"
@implementation Person
- (void)test
{
NSLog(@"test print name is : %@", self.name);
}
@end
// ViewController.m
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [Person class];
void *obj = &cls;
[(__bridge id)obj test];
Person *person = [[Person alloc] init];
[person test];
}
複製代碼
這道面試題確實很無厘頭的一道題,平常工做中沒有人這樣寫代碼,可是須要解答這道題須要很完備的底層知識,咱們經過這道題來複習一下,首先看一下打印結果。
Runtime面試題[15842:2579705] test print name is : <ViewController: 0x7f95514077a0>
Runtime面試題[15842:2579705] test print name is : (null)
複製代碼
經過上述打印結果咱們能夠看出,是能夠正常運行並打印的,說明obj
能夠正常調用test
方法,可是咱們發現打印self.name
的內容倒是<ViewController: 0x7f95514077a0>
。下面person
實例調用test
不作過多解釋了,主要用來和上面方法調用作對比。
爲何會是這樣的結果呢?首先經過一張圖看一下兩種調用方法的內存信息。
經過上圖咱們能夠發現兩種方法調用方式很相近。那麼obj爲何能夠正常調用方法?
首先經過以前的學習咱們知道,person
調用方法時首先經過isa
指針找到類對象進而查找方法並進行調用。
而person
實例對象內其實是取最前面8個字節空間也就是isa
並經過計算得出類對象地址。
而經過上圖咱們能夠發現,obj
在調用test
方法時,也會經過其內存地址找到cls
,而cls
中取出最前面8個字節空間其內部存儲的恰好是Person
類對象地址。所以obj
是能夠正常調用方法的。
問題出在[super viewDidLoad];
這段代碼中,經過上述對super
本質的分析咱們知道,super
內部調用objc_msgSendSuper2
函數。
咱們知道objc_msgSendSuper2
函數內部會傳入兩個參數,objc_super2
結構體和SEL
,而且objc_super2
結構體內有兩個成員變量消息接受者和其父類。
struct objc_super2 {
id receiver; // 消息接受者
Class current_class; // 當前類
};
};
複製代碼
經過以上分析咱們能夠得知[super viewDidLoad];
內部objc_super2
結構體內存儲以下所示
struct objc_super = {
self,
[ViewController Class]
};
複製代碼
那麼objc_msgSendSuper2
函數調用以前,會先建立局部變量objc_super2
結構體用於爲objc_msgSendSuper2
函數傳遞的參數。
咱們知道局部變量是存儲在棧空間內的,而且是由高地址向低地址有序存儲。 咱們經過一段代碼驗證一下。
long long a = 1;
long long b = 2;
long long c = 3;
NSLog(@"%p %p %p", &a,&b,&c);
// 打印內容
0x7ffee9774958 0x7ffee9774950 0x7ffee9774948
複製代碼
經過上述代碼打印內容,咱們能夠驗證局部變量在棧空間內是由高地址向低地址連續存儲的。
那麼咱們回到面試題中,經過上述分析咱們知道,此時代碼中包含局部變量以此爲objc_super2 結構體
、cls
、obj
。經過一張圖展現一下這些局部變量存儲結構。
上面咱們知道當person
實例對象調用方法的時候,會取實例變量前8個字節空間也就是isa
來找到類對象地址。那麼當訪問實例變量的時候,就跳過isa
的8個字節空間往下面去找實例變量。
那麼當obj
在調用test
方法的時候一樣找到cls
中取出前8個字節,也就是Person類對象
的內存地址,那麼當訪問實例變量_name
的時候,會繼續向高地址內存空間查找,此時就會找到objc_super
結構體,從中取出8個字節空間也就是self
,所以此時訪問到的self.name
就是ViewController對象
。
當訪問成員變量_name
的時候,test
函數中的self
也就是方法調用者實際上是obj
,那麼self.name
就是經過obj
去找_name
,跳過cls的8個指針,在取8個指針此時天然獲取到ViewController對象
。
所以上述代碼中cls
就至關於isa
,isa
下面的8個字節空間就至關於_name
成員變量。所以成員變量_name
的訪問到的值就是cls
地址後向高地址位取8個字節地址空間存儲的值。
爲了驗證上述說法,咱們作一個實驗,在cls
後高地址中添加一個string
,那麼此時cls
下面的高地址位就是string
。如下示例代碼
- (void)viewDidLoad {
[super viewDidLoad];
NSString *string = @"string";
id cls = [Person class];
void *obj = &cls;
[(__bridge id)obj test];
Person *person = [[Person alloc] init];
[person test];
}
複製代碼
此時的局部變量內存結構以下圖所示
此時在訪問_name
成員變量的時候,越過cls
內存往高地址找就會來到string
,此時拿到的成員變量就是string
了。 咱們來看一下打印內容
Runtime面試題[16887:2829028] test print name is : string
Runtime面試題[16887:2829028] test print name is : (null)
複製代碼
再經過一段代碼使用int數據進行試驗
- (void)viewDidLoad {
[super viewDidLoad];
int a = 3;
id cls = [Person class];
void *obj = &cls;
[(__bridge id)obj test];
Person *person = [[Person alloc] init];
[person test];
}
// 程序crash,壞地址訪問
複製代碼
咱們發現程序由於壞地址訪問而crash,此時局部變量內存結構以下圖所示
當須要訪問_name
成員變量的時候,會在cls
後高地址爲查找8位的字節空間,而咱們知道int
佔4位字節,那麼此時8位的內存空間同時佔據int
數據及objc_super
結構體內,所以就會形成壞地址訪問而crash。
咱們添加新的成員變量進行訪問
// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *nickName;
- (void)test;
@end
------------
// Person.m
#import "Person.h"
@implementation Person
- (void)test
{
NSLog(@"test print name is : %@", self.nickName);
}
@end
--------
// ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *obj1 = [[NSObject alloc] init];
id cls = [Person class];
void *obj = &cls;
[(__bridge id)obj test];
Person *person = [[Person alloc] init];
[person test];
}
複製代碼
咱們看一下打印內容
// 打印內容
// Runtime面試題[17272:2914887] test print name is : <ViewController: 0x7ffc6010af50>
// Runtime面試題[17272:2914887] test print name is : (null)
複製代碼
能夠發現此時打印的仍然是ViewController對象
,咱們先來看一下其局部變量內存結構
首先經過obj
找到cls
,cls
找到類對象進行方法調用,此時在訪問nickName
時,obj
查找成員變量,首先跳過8個字節的cls
,以後跳過name
所佔的8個字節空間,最終再取8個字節空間取出其中的值做爲成員變量的值,那麼此時也就是self
了。
總結:這道面試題雖然很無厘頭,讓人感受無從下手可是考察的內容很是多。 1. super的底層本質爲調用objc_msgSendSuper2
函數,傳入objc_super2
結構體,結構體內部存儲消息接受者和當前類,用來告知系統方法查找從父類開始。
2. 局部變量分配在棧空間,而且從高地址向低地址連續分配。先建立的局部變量分配在高地址,後續建立的局部變量連續分配在較低地址。
3. 方法調用的消息機制,經過isa指針找到類對象進行消息發送。
4. 指針存儲的是實例變量的首字節地址,上述例子中person
指針存儲的其實就是實例變量內部的isa
指針的地址。
5. 訪問成員變量的本質,找到成員變量的地址,按照成員變量所佔的字節數,取出地址中存儲的成員變量的值。
咱們使用如下代碼來驗證上文中遺留的問題
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [Person class];
void *obj = &cls;
[(__bridge id)obj test];
}
複製代碼
上述代碼的局部變量內存結構咱們以前已經分析過了,真正的內存結構應該以下圖所示
經過上面對面試題的分析,咱們如今想要驗證objc_msgSendSuper2
函數內傳入的結構體參數,只須要拿到cls
的地址,而後向後移8個地址就能夠獲取到objc_super
結構體內的self
,在向後移8個地址就是current_class
的內存地址。經過打印current_class
的內容,就能夠知道傳入objc_msgSendSuper2
函數內部的是當前類對象仍是父類對象了。
咱們來證實他是UIViewController
仍是ViewController
便可
經過上圖能夠發現,最終打印的內容確實爲當前類對象。 所以objc_msgSendSuper2
函數內部其實傳入的是當前類對象,而且在函數內部獲取其父類,告知系統從父類方法開始查找的。
首先咱們經過來看一段代碼,後續Runtime API的使用均基於此代碼。
// Person類繼承自NSObject,包含run方法
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
- (void)run;
@end
#import "Person.h"
@implementation Person
- (void)run
{
NSLog(@"%s",__func__);
}
@end
// Car類繼承自NSObejct,包含run方法
#import "Car.h"
@implementation Car
- (void)run
{
NSLog(@"%s",__func__);
}
@end
複製代碼
1. 動態建立一個類(參數:父類,類名,額外的內存空間)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
2. 註冊一個類(要在類註冊以前添加成員變量)
void objc_registerClassPair(Class cls)
3. 銷燬一個類
void objc_disposeClassPair(Class cls)
示例:
void run(id self , SEL _cmd) {
NSLog(@"%@ - %@", self,NSStringFromSelector(_cmd));
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 建立類 superclass:繼承自哪一個類 name:類名 size_t:格外的大小,建立類是否須要擴充空間
// 返回一個類對象
Class newClass = objc_allocateClassPair([NSObject class], "Student", 0);
// 添加成員變量
// cls:添加成員變量的類 name:成員變量的名字 size:佔據多少字節 alignment:內存對齊,最好寫1 types:類型,int類型就是@encode(int) 也就是i
class_addIvar(newClass, "_age", 4, 1, @encode(int));
class_addIvar(newClass, "_height", 4, 1, @encode(float));
// 添加方法
class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
// 註冊類
objc_registerClassPair(newClass);
// 建立實例對象
id student = [[newClass alloc] init];
// 經過KVC訪問
[student setValue:@10 forKey:@"_age"];
[student setValue:@180.5 forKey:@"_height"];
// 獲取成員變量
NSLog(@"_age = %@ , _height = %@",[student valueForKey:@"_age"], [student valueForKey:@"_height"]);
// 獲取類的佔用空間
NSLog(@"類對象佔用空間%zd", class_getInstanceSize(newClass));
// 調用動態添加的方法
[student run];
}
return 0;
}
// 打印內容
// Runtime應用[25605:4723961] _age = 10 , _height = 180.5
// Runtime應用[25605:4723961] 類對象佔用空間16
// Runtime應用[25605:4723961] <Student: 0x10072e420> - run
注意
類一旦註冊完畢,就至關於類對象和元類對象裏面的結構就已經建立好了。
所以必須在註冊類以前,添加成員變量。方法能夠在註冊以後再添加,由於方法是能夠動態添加的。
建立的類若是不須要使用了 ,須要釋放類。
複製代碼
4. 獲取isa指向的Class,若是將類對象傳入獲取的就是元類對象,若是是實例對象則爲類對象
Class object_getClass(id obj)
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
NSLog(@"%p,%p,%p",object_getClass(person), [Person class],
object_getClass([Person class]));
}
return 0;
}
// 打印內容
Runtime應用[21115:3807804] 0x100001298,0x100001298,0x100001270
複製代碼
5. 設置isa指向的Class,能夠動態的修改類型。例如修改了person對象的類型,也就是說修改了person對象的isa指針的指向,中途讓對象去調用其餘類的同名方法。
Class object_setClass(id obj, Class cls)
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
[person run];
object_setClass(person, [Car class]);
[person run];
}
return 0;
}
// 打印內容
Runtime應用[21147:3815155] -[Person run]
Runtime應用[21147:3815155] -[Car run]
最終其實調用了car的run方法
複製代碼
6. 用於判斷一個OC對象是否爲Class
BOOL object_isClass(id obj)
// 判斷OC對象是實例對象仍是類對象
NSLog(@"%d",object_isClass(person)); // 0
NSLog(@"%d",object_isClass([person class])); // 1
NSLog(@"%d",object_isClass(object_getClass([person class]))); // 1
// 元類對象也是特殊的類對象
複製代碼
7. 判斷一個Class是否爲元類
BOOL class_isMetaClass(Class cls)
8. 獲取類對象父類
Class class_getSuperclass(Class cls)
複製代碼
1. 獲取一個實例變量信息,描述信息變量的名字,佔用多少字節等
Ivar class_getInstanceVariable(Class cls, const char *name)
2. 拷貝實例變量列表(最後須要調用free釋放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
3. 設置和獲取成員變量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)
4. 動態添加成員變量(已經註冊的類是不能動態添加成員變量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
5. 獲取成員變量的相關信息,傳入成員變量信息,返回C語言字符串
const char *ivar_getName(Ivar v)
6. 獲取成員變量的編碼,types
const char *ivar_getTypeEncoding(Ivar v)
示例:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 獲取成員變量的信息
Ivar nameIvar = class_getInstanceVariable([Person class], "_name");
// 獲取成員變量的名字和編碼
NSLog(@"%s, %s", ivar_getName(nameIvar), ivar_getTypeEncoding(nameIvar));
Person *person = [[Person alloc] init];
// 設置和獲取成員變量的值
object_setIvar(person, nameIvar, @"xx_cc");
// 獲取成員變量的值
object_getIvar(person, nameIvar);
NSLog(@"%@", object_getIvar(person, nameIvar));
NSLog(@"%@", person.name);
// 拷貝實例變量列表
unsigned int count ;
Ivar *ivars = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i ++) {
// 取出成員變量
Ivar ivar = ivars[i];
NSLog(@"%s, %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
}
free(ivars);
}
return 0;
}
// 打印內容
// Runtime應用[25783:4778679] _name, @"NSString"
// Runtime應用[25783:4778679] xx_cc
// Runtime應用[25783:4778679] xx_cc
// Runtime應用[25783:4778679] _name, @"NSString"
複製代碼
1. 獲取一個屬性
objc_property_t class_getProperty(Class cls, const char *name)
2. 拷貝屬性列表(最後須要調用free釋放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
3. 動態添加屬性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
4. 動態替換屬性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
5. 獲取屬性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
複製代碼
1. 得到一個實例方法、類方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
2. 方法實現相關操做
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2)
3. 拷貝方法列表(最後須要調用free釋放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
4. 動態添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
5. 動態替換方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
6. 獲取方法的相關信息(帶有copy的須要調用free去釋放)
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char *method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index)
7. 選擇器相關
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
8. 用block做爲方法實現
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
複製代碼
關於Runtime的應用請參考iOS-RunTime應用
文中若是有不對的地方歡迎指出。我是xx_cc,一隻長大好久但尚未二夠的傢伙。須要視頻一塊兒探討學習的coder能夠加我Q:2336684744