前言
本篇文章只講Method
的特性及相關(guān)方法颂鸿,不講Method Swizzling
特性吗冤。關(guān)于Method Swizzling
特性,我們放在單獨(dú)的一篇文章來細(xì)講问顷,因為這一節(jié)非常重要。
Method類型
Method類型是一個objc_method
結(jié)構(gòu)體指針禀梳,而結(jié)構(gòu)體objc_method
有三個成員:
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;
struct objc_method {
SEL method_name; // 方法名稱
char *method_typesE; // 參數(shù)和返回類型的描述字串
IMP method_imp; // 方法的具體的實現(xiàn)的指針
}
Method所有方法
下面是官方所提供的所有Method的方法杜窄,我們一一說明其用途,看代碼注釋:
// 函數(shù)調(diào)用算途,但是不接收返回值類型為結(jié)構(gòu)體
method_invoke
// 函數(shù)調(diào)用塞耕,但是接收返回值類型為結(jié)構(gòu)體
method_invoke_stret
// 獲取函數(shù)名
method_getName
// 獲取函數(shù)實現(xiàn)IMP
method_getImplementation
// 獲取函數(shù)type encoding
method_getTypeEncoding
// 復(fù)制返回值類型
method_copyReturnType
// 復(fù)制參數(shù)類型
method_copyArgumentType
// 獲取返回值類型
method_getReturnType
// 獲取參數(shù)個數(shù)
method_getNumberOfArguments
// 獲取函數(shù)參數(shù)類型
method_getArgumentType
// 獲取函數(shù)描述
method_getDescription
// 設(shè)置函數(shù)實現(xiàn)IMP
method_setImplementation
// 交換函數(shù)的實現(xiàn)IMP
method_exchangeImplementations
獲取函數(shù)列表
我們嘗試獲取函數(shù)列表,并細(xì)說函數(shù)的參數(shù)type encoding嘴瓤、返回值類型等扫外。我們先寫以下幾個方法:
- (int)testInstanceMethod:(NSString *)name andValue:(NSNumber *)value {
NSLog(@"%@", name);
return value.intValue;
}
- (NSArray *)arrayWithNames:(NSArray *)names {
NSLog(@"%@", names);
return names;
}
- (void)getMethods {
unsigned int outCount = 0;
Method *methodList = class_copyMethodList(self.class, &outCount);
for (unsigned int i = 0; i < outCount; ++i) {
Method method = methodList[i];
SEL methodName = method_getName(method);
NSLog(@"方法名:%@", NSStringFromSelector(methodName));
// 獲取方法的參數(shù)類型
unsigned int argumentsCount = method_getNumberOfArguments(method);
char argName[512] = {};
for (unsigned int j = 0; j < argumentsCount; ++j) {
method_getArgumentType(method, j, argName, 512);
NSLog(@"第%u個參數(shù)類型為:%s", j, argName);
memset(argName, '\0', strlen(argName));
}
char returnType[512] = {};
method_getReturnType(method, returnType, 512);
NSLog(@"返回值類型:%s", returnType);
// type encoding
NSLog(@"TypeEncoding: %s", method_getTypeEncoding(method));
}
free(methodList);
}
然后,我們寫一個測試方法來調(diào)用一下:
+ (void)test {
GGMethodLearn *m = [[GGMethodLearn alloc] init];
[m getMethods];
// 這就是為什么有四個參數(shù)的原因
int returnValue = ((int (*)(id, SEL, NSString *, NSNumber *))objc_msgSend)((id)m, @selector(testInstanceMethod:andValue:), @"技術(shù)博客", @100);
NSLog(@"return value is %d", returnValue);
}
其打印結(jié)果如下:
方法名:getMethods
第0個參數(shù)類型為:@
第1個參數(shù)類型為::
返回值類型:v
TypeEncoding: v16@0:8
方法名:testInstanceMethod:andValue:
第0個參數(shù)類型為:@
第1個參數(shù)類型為::
第2個參數(shù)類型為:@
第3個參數(shù)類型為:@
返回值類型:i
TypeEncoding: i32@0:8@16@24
方法名:arrayWithNames:
第0個參數(shù)類型為:@
第1個參數(shù)類型為::
第2個參數(shù)類型為:@
返回值類型:@
TypeEncoding: @24@0:8@16
技術(shù)博客
return value is 100
從打印結(jié)果中廓脆,可以看到好多好奇怪的符號筛谚。
獲取函數(shù)名
我們通過method_getName()
函數(shù)來獲取方法SEL
:
SEL methodName = method_getName(method);
NSLog(@"方法名:%@", NSStringFromSelector(methodName));
為什么多了兩個參數(shù)
通過method_getNumberOfArguments
獲取到函數(shù)的所有參數(shù)類型,從上面我們所定義的函數(shù)中停忿,比如getMethods
明明沒有參數(shù)驾讲,為什么會打印出來兩個參數(shù)呢?而- (int)testInstanceMethod:(NSString *)name andValue:(NSNumber *)value
明明只有兩個參數(shù)席赂,為什么有四個參數(shù)呢吮铭?下面我們來細(xì)說:
首先,對于第一個方法颅停,它在編譯時會被轉(zhuǎn)換成類似這樣:
((void (*)(id, SEL))objc_msgSend)((id)m, @selector(getMethods));
這樣一看谓晌,知道是有兩個參數(shù)了吧?
同樣的道理癞揉,對于第二個方法纸肉,在編譯時編譯器會將其轉(zhuǎn)換成類似這樣:
// 這就是為什么有四個參數(shù)的原因
int returnValue = ((int (*)(id, SEL, NSString *, NSNumber *))
objc_msgSend)((id)m,
@selector(testInstanceMethod:andValue:),
@"技術(shù)博客",
@100);
函數(shù)編碼
通過method_getTypeEncoding
獲取函數(shù)的編碼,其結(jié)果是一串值烧董。
- 第一個方法的編碼為:v16@0:8
- 第二個方法的編碼為:i32@0:8@16@24
- 第三個方法的編碼為:@24@0:8@16
這么一看毁靶,可以看出來什么呢?從這幾個值可以看出:
- 第一個位置是返回值類型,比如第一個方法返回值是V,第二個的是i橘蜜,第三個的是@
- 第二/三個位置是第一/二個參數(shù)蒜撮,參數(shù)列表從左到右算访圃。分別是@ :救氯,@ :吩坝,@ :控轿,都是對象凤瘦,其實第一個和第二個參數(shù)是固定的宿礁,第一個是接收消息的對象,而第二個是方法選擇器SEL蔬芥。
- 如果還有其它參數(shù)梆靖,依次...
但是類型后面跟著的數(shù)字是什么呢?其實筆者也不清楚笔诵,文檔沒有明確說明返吻,不過從其打印結(jié)果可以看得出來其規(guī)律。比如第一個方法的:@的偏移為0乎婿、:的偏移為8测僵、v的偏移為16,其它方法也是類似谢翎。
method_invoke
除了使用objc_msgSend
函數(shù)之外捍靠,還可以使用method_invoke
,如:
// 獲取方法
Method method = class_getInstanceMethod([self class], @selector(testInstanceMethod:andValue:));
// 調(diào)用函數(shù)
returnValue = ((int (*)(id, Method, NSString *, NSNumber *))method_invoke)((id)m, method, @"測試使用method_invoke", @11);
NSLog(@"call return vlaue is %d", returnValue);
// 與下面的調(diào)用可以得到同樣的效果
int returnValue = ((int (*)(id, SEL, NSString *, NSNumber *))
objc_msgSend)((id)m,
@selector(testInstanceMethod:andValue:),
@"技術(shù)博客",
@100);
NSLog(@"return value is %d", returnValue);
Type Encoding
下面是官方給出的所有類型編碼森逮,數(shù)據(jù)類型的編碼最終值會有可能是下面中的多個的組合:
編碼值 | 含意 |
---|---|
c | 代表char類型 |
i | 代表int類型 |
s | 代表short類型 |
l | 代表long類型榨婆,在64位處理器上也是按照32位處理 |
q | 代表long long類型 |
C | 代表unsigned char類型 |
I | 代表unsigned int類型 |
S | 代表unsigned short類型 |
L | 代表unsigned long類型 |
Q | 代表unsigned long long類型 |
f | 代表float類型 |
d | 代表double類型 |
B | 代表C++中的bool或者C99中的_Bool |
v | 代表void類型 |
* | 代表char *類型 |
@ | 代表對象類型 |
# | 代表類對象 (Class) |
: | 代表方法selector (SEL) |
[array type] | 代表array |
{name=type...} | 代表結(jié)構(gòu)體 |
(name=type...) | 代表union |
bnum | A bit field of num bits |
^type | A pointer to type |
? | An unknown type (among other things, this code is used for function pointers) |
demo:RuntimeDemo