轉(zhuǎn)載標(biāo)哥的一篇博客, 寫的真是詳細(xì)
Method類型
Method類型是一個(gè)
objc_method
結(jié)構(gòu)體指針四啰,而結(jié)構(gòu)體objc_method
有三個(gè)成員:
/// 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; // 方法的具體的實(shí)現(xiàn)的指針
}
Method所有方法
// 函數(shù)調(diào)用睹逃,但是不接收返回值類型為結(jié)構(gòu)體
method_invoke
// 函數(shù)調(diào)用,但是接收返回值類型為結(jié)構(gòu)體
method_invoke_stret
// 獲取函數(shù)名
method_getName
// 獲取函數(shù)實(shí)現(xiàn)IMP
method_getImplementation
// 獲取函數(shù)type encoding
method_getTypeEncoding
// 復(fù)制返回值類型
method_copyReturnType
// 復(fù)制參數(shù)類型
method_copyArgumentType
// 獲取返回值類型
method_getReturnType
// 獲取參數(shù)個(gè)數(shù)
method_getNumberOfArguments
// 獲取函數(shù)參數(shù)類型
method_getArgumentType
// 獲取函數(shù)描述
method_getDescription
// 設(shè)置函數(shù)實(shí)現(xiàn)IMP
method_setImplementation
// 交換函數(shù)的實(shí)現(xiàn)IMP
method_exchangeImplementations
獲取函數(shù)列表
- (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個(gè)參數(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);
}
我們寫一個(gè)測(cè)試方法來調(diào)用一下:
+ (void)test {
HYBMethodLearn *m = [[HYBMethodLearn alloc] init];
[m getMethods];
// 這就是為什么有四個(gè)參數(shù)的原因
int returnValue = ((int (*)(id, SEL, NSString *, NSNumber *))objc_msgSend)((id)m, @selector(testInstanceMethod:andValue:), @"標(biāo)哥的技術(shù)博客", @100);
NSLog(@"return value is %d", returnValue);
}
其打印結(jié)果如下:
方法名:getMethods
第0個(gè)參數(shù)類型為:@
第1個(gè)參數(shù)類型為::
返回值類型:v
TypeEncoding: v16@0:8
方法名:testInstanceMethod:andValue:
第0個(gè)參數(shù)類型為:@
第1個(gè)參數(shù)類型為::
第2個(gè)參數(shù)類型為:@
第3個(gè)參數(shù)類型為:@
返回值類型:i
TypeEncoding: i32@0:8@16@24
方法名:arrayWithNames:
第0個(gè)參數(shù)類型為:@
第1個(gè)參數(shù)類型為::
第2個(gè)參數(shù)類型為:@
返回值類型:@
TypeEncoding: @24@0:8@16
標(biāo)哥的技術(shù)博客
return value is 100
為什么多了兩個(gè)參數(shù)
通過method_getNumberOfArguments獲取到函數(shù)的所有參數(shù)類型据途,從上面我們所定義的函數(shù)中欣孤,比如getMethods明明沒有參數(shù)嫉沽,為什么會(huì)打印出來兩個(gè)參數(shù)呢?而- (int)testInstanceMethod:(NSString *)name andValue:(NSNumber *)value明明只有兩個(gè)參數(shù)械蹋,為什么有四個(gè)參數(shù)呢出皇?下面我們來細(xì)說:
- 首先,對(duì)于第一個(gè)方法哗戈,它在編譯時(shí)會(huì)被轉(zhuǎn)換成類似這樣:
((void (*)(id, SEL))objc_msgSend)((id)m, @selector(getMethods));
- 同樣的道理郊艘,對(duì)于第二個(gè)方法,在編譯時(shí)編譯器會(huì)將其轉(zhuǎn)換成類似這樣:
// 這就是為什么有四個(gè)參數(shù)的原因
int returnValue = ((int (*)(id, SEL, NSString *, NSNumber *))
objc_msgSend)((id)m,
@selector(testInstanceMethod:andValue:),
@"標(biāo)哥的技術(shù)博客",
@100);
函數(shù)編碼
通過method_getTypeEncoding獲取函數(shù)的編碼唯咬,其結(jié)果是一串值纱注。
- 第一個(gè)方法的編碼為:v16@0:8
- 第二個(gè)方法的編碼為:i32@0:8@16@24
- 第三個(gè)方法的編碼為:@24@0:8@16
這么一看,可以看出來什么呢胆胰?從這幾個(gè)值可以看出:
- 第一個(gè)位置是返回值類型狞贱,比如第一個(gè)方法返回值是V,第二個(gè)的是i蜀涨,第三個(gè)的是@
- 第二/三個(gè)位置是第一/二個(gè)參數(shù)瞎嬉,參數(shù)列表從左到右算。分別是@ :厚柳,@ :氧枣,@ :,都是對(duì)象草娜,其實(shí)第一個(gè)和第二個(gè)參數(shù)是固定的挑胸,第一個(gè)是接收消息的對(duì)象痒筒,而第二個(gè)是方法選擇器SEL宰闰。
- 如果還有其它參數(shù),依次…
但是類型后面跟著的數(shù)字是什么呢簿透?其實(shí)筆者也不清楚移袍,文檔沒有明確說明,不過從其打印結(jié)果可以看得出來其規(guī)律老充。比如第一個(gè)方法的:@的偏移為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, @"測(cè)試使用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:),
@"標(biāo)哥的技術(shù)博客",
@100);
NSLog(@"return value is %d", returnValue);
Type Encoding
編碼值 | 含意 |
---|---|
c | 代表char類型 |
i | 代表int類型 |
s | 代表short類型 |
l(小寫L) | 代表long類型喘先,在64位處理器上也是按照32位處理 |
q | 代表long long類型 |
C | 代表unsigned char類型 |
I(大寫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 *類型 |
@ | 代表對(duì)象類型 |
# | 代表類對(duì)象 (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) |