[英]NSInvocation with block arguments
我正在嘗試將塊參數傳遞給NSInvocation
,但應用程序崩潰了。 調用發出網絡請求並調用成功或失敗塊。 我認為問題是在網絡請求完成之前會釋放塊。 我設法讓它與一些Block_copy
hackery一起工作,並且它不使用Instruments報告任何泄漏。
問題: - 即使靜態分析儀或儀器沒有報告,泄漏是否可能存在? - 有沒有更好的方法來“保留”這個區塊?
// Create the NSInvocation
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
NSInvocation* invoc = [NSInvocation invocationWithMethodSignature:methodSignature];
[invoc setTarget:target];
[invoc setSelector:selector];
// Create success and error blocks.
void (^successBlock)(id successResponse) = ^(id successResponse) {
// Some success code here ...
};
void (^errorBlock)(NSError *error) = ^(NSError *error) {
// Some failure code here ...
};
/*
Without the two Block_copy lines, the block gets dealloced too soon
and the app crashes with EXC_BAD_ACCESS
I tried [successBlock copy] and [failureBlock copy] instead,
but the app still crashes.
It seems like Block_copy is the only way to move the block to the heap in this case.
*/
Block_copy((__bridge void *)successBlock);
Block_copy((__bridge void *)errorBlock);
// Set the success and failure blocks.
[invoc setArgument:&successBlock atIndex:2];
[invoc setArgument:&errorBlock atIndex:3];
[invoc retainArguments]; // does not retain blocks
// Invoke the method.
[invoc invoke];
更新:我將代碼更新到下面。 塊是NSMallocBlocks
,但應用程序仍然崩潰。
// Create success and error blocks.
int i = 0;
void (^successBlock)(id successResponse) = ^(id successResponse) {
NSLog(@"i = %i", i);
// Some success code here ...
};
void (^errorBlock)(NSError *error) = ^(NSError *error) {
NSLog(@"i = %i", i);
// Some failure code here ...
};
/*** Both blocks are NSMallocBlocks here ***/
// Set the success and failure blocks.
void (^successBlockCopy)(id successResponse) = [successBlock copy];
void (^errorBlockCopy)(NSError *error) = [errorBlock copy];
/*** Both blocks are still NSMallocBlocks here - I think copy is a NoOp ***/
// Set the success and failure blocks.
[invoc setArgument:&successBlockCopy atIndex:2];
[invoc setArgument:&errorBlockCopy atIndex:3];
[invoc retainArguments]; // does not retain blocks
// Invoke the method.
[invoc invoke];
這些塊在鏈中傳遞如下:
NSInvocation
→ NSProxy
(使用forwardInvocation:
NSInvocation
forwardInvocation:
)→ method1
→ methodN
methodN
最終根據HTTP響應調用成功或失敗塊。
我是否需要在每個階段復制塊? 上面的例子討論了第一個NSInvocation
。 我還需要[invocation retainArguments];
在每個適當的步驟? 我正在使用ARC。
Block_copy
,實際上[block copy]
返回副本。 他們不會在同一位置用副本神奇地切換原件。 所以至少我認為你想要:
successBlock = Block_copy((__bridge void *)successBlock);
errorBlock = Block_copy((__bridge void *)errorBlock);
(或者,相當於, successBlock = [successBlock copy]; ...
)
否則,您正在創建副本,不對它們執行任何操作,仍然將原始文件傳遞給調用。
編輯:所以,我把以下代碼放入一個項目:
@interface DummyClass: NSObject
@end
typedef void (^ successBlock)(id successResponse);
typedef void (^ failureBlock)(NSError *error);
@implementation DummyClass
- (id)init
{
self = [super init];
if(self)
{
SEL selector = @selector(someMethodWithSuccess:failure:);
id target = self;
// Create the NSInvocation
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
NSInvocation* invoc = [NSInvocation invocationWithMethodSignature:methodSignature];
[invoc setTarget:target];
[invoc setSelector:selector];
// Create success and error blocks.
void (^successBlock)(id successResponse) = ^(id successResponse) {
// Some success code here ...
NSLog(@"Off, off, off with %@", successResponse);
};
void (^errorBlock)(NSError *error) = ^(NSError *error) {
// Some failure code here ...
NSLog(@"Dance, dance, dance till %@", error);
};
successBlock = [successBlock copy];
errorBlock = [errorBlock copy];
// Set the success and failure blocks.
[invoc setArgument:&successBlock atIndex:2];
[invoc setArgument:&errorBlock atIndex:3];
[invoc retainArguments]; // does not retain blocks
// Invoke the method.
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(),
^{
[invoc invoke];
});
}
return self;
}
- (void)someMethodWithSuccess:(successBlock)successBlock failure:(failureBlock)failureBlock
{
NSLog(@"Words:");
successBlock(@[@"your", @"head"]);
failureBlock([NSError errorWithDomain:@"you're dead" code:0 userInfo:nil]);
}
@end
並在application:didFinishLaunchingWithOptions:
結束時添加以下內容application:didFinishLaunchingWithOptions:
::
DummyClass *unusedInstance = [[DummyClass alloc] init];
結果是在啟動程序兩秒后,控制台上出現以下內容:
2013-06-02 20:11:56.057 TestProject[3330:c07] Words:
2013-06-02 20:11:56.059 TestProject[3330:c07] Off, off, off with (
your,
head
)
2013-06-02 20:11:56.060 TestProject[3330:c07] Dance, dance, dance till Error Domain=you're dead Code=0 "The operation couldn’t be completed. (you're dead error 0.)"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.