注:方法的參數(shù)及返回值需為對(duì)象,否則id接收的時(shí)候會(huì)報(bào)錯(cuò)
在學(xué)習(xí)NSInvocation的時(shí)候,給NSObject添加了一個(gè)category方法,如下所示
/** 系統(tǒng)提供的perform系列方法參數(shù)個(gè)數(shù)有限,可以利用NSInvocation實(shí)現(xiàn)多參數(shù) */
- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects {
// 初始化方法簽名
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
// 如果方法不存在
if (!signature) {
// 拋出異常
NSString *reason = [NSString stringWithFormat:@"方法不存在 : %@",NSStringFromSelector(aSelector)];
@throw [NSException exceptionWithName:@"error" reason:reason userInfo:nil];
}
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = aSelector;
// 參數(shù)個(gè)數(shù)signature.numberOfArguments 默認(rèn)有一個(gè)_cmd 一個(gè)target 所以要-2
NSInteger paramsCount = signature.numberOfArguments - 2;
// 當(dāng)objects的個(gè)數(shù)多于函數(shù)的參數(shù)的時(shí)候,取前面的參數(shù)
// 當(dāng)objects的個(gè)數(shù)少于函數(shù)的參數(shù)的時(shí)候,不需要設(shè)置,默認(rèn)為nil
paramsCount = MIN(paramsCount, objects.count);
for (NSInteger index = 0; index < paramsCount; index++) {
id object = objects[index];
// 對(duì)參數(shù)為nil的處理
if ([object isKindOfClass:[NSNull class]]) {
continue;
}
[invocation setArgument:&object atIndex:index + 2];
}
// 調(diào)用方法
[invocation invoke];
// 獲取返回值
id returnValue = nil;
//signature.methodReturnLength == 0 說(shuō)明給方法沒(méi)有返回值
if (signature.methodReturnLength) {
//獲取返回值
[invocation getReturnValue:&returnValue];
}
return returnValue;
}
然后美滋滋的寫(xiě)了一個(gè)多參數(shù)的方法
#pragma mark - Test Method
- (NSString *)connectStrings:(NSString *)a b:(NSString *)b c:(NSString *)c {
return [NSString stringWithFormat:@"%@%@%@",a,b,c];
}
調(diào)用
NSString *str = [self performSelector:@selector(connectStrings:b:c:) withObjects:@[@"hello ",@"everyone,",@"good morning"]];
NSLog(@"%@",str);
當(dāng)當(dāng)當(dāng)當(dāng)!!! Crash! 而且控制臺(tái)沒(méi)有打印任何錯(cuò)誤信息!小萌新當(dāng)場(chǎng)懵逼!
經(jīng)過(guò)我一系列的縝(bai)密(du)分(sou)析(suo),問(wèn)題得以解決.步驟如下
1.打開(kāi)僵尸模式調(diào)試,得以打印出崩潰信息
*** -[CFString release]: message sent to deallocated instance 0x6000008488e0
2.在這里已經(jīng)大致猜測(cè)到可能是方法返回值被提前釋放了 于是打印地址信息
//打印對(duì)象的內(nèi)存地址
NSLog(@"內(nèi)存地址1:%p",str);
//打印指針自己的內(nèi)存地址
NSLog(@"內(nèi)存地址2:%x",&str);
對(duì)比之后發(fā)現(xiàn)剛好是返回值的內(nèi)存地址,那么什么原因?qū)е碌哪?
3.于是又經(jīng)過(guò)一波縝(bai)密(du)分(sou)析(suo),發(fā)現(xiàn)這一段代碼存在問(wèn)題
// 獲取返回值
id returnValue = nil;
//signature.methodReturnLength == 0 說(shuō)明給方法沒(méi)有返回值
if (signature.methodReturnLength) {
//獲取返回值
[invocation getReturnValue:&returnValue];
}
return returnValue;
原因是在arc模式下缝呕,getReturnValue:僅僅是從invocation的返回值拷貝到指定的內(nèi)存地址,如果返回值是一個(gè)NSObject對(duì)象的話,是沒(méi)有處理起內(nèi)存管理的。而我們?cè)诙xreturnValue時(shí)使用的是__strong類型的指針對(duì)象,arc就會(huì)假設(shè)該內(nèi)存塊已被retain(實(shí)際沒(méi)有)雹姊,當(dāng)returnValue出了定義域釋放時(shí),導(dǎo)致該crash。假如在定義之前有賦值的話手销,還會(huì)造成內(nèi)存泄露的問(wèn)題。
修改如下
// 獲取返回值
id __unsafe_unretained returnValue = nil;
//signature.methodReturnLength == 0 說(shuō)明給方法沒(méi)有返回值
if (signature.methodReturnLength) {
//獲取返回值
[invocation getReturnValue:&returnValue];
}
id value = returnValue;
return value;
或者
void *returnValue = NULL;
if (signature.methodReturnLength) {
[invocation getReturnValue:&returnValue];
}
return (__bridge id)returnValue;
至此,問(wèn)題解決.