与块和幻像块参数交换的奇怪崩溃

时间:2011-07-20 13:50:14

标签: objective-c cocoa objective-c-blocks

我对这个感到非常难过,希望有人可以帮助我:

A类

- (void)setBlock:(BOOL(^)(id sender))block {
    myBlock = Block_copy(block);
}

- (BOOL)runBlock:(id)sender {
    myBlock(sender);
}

B类

- (void)applicationDidFinishLaunching:(NSNotification *)aNotificationx {
    //The outer block provides behaviour according to strategy pattern:
    [classAInstance setBlock:^BOOL(id sender) {
        NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
        //The inner block is a special case of behaviour where I want the task to run asynchronously:
        [queue addOperationWithBlock:^(void) {
            NSLog(@"sender: %@", [sender class]);
            [sender doSomething];
        }];
        return YES;
    }];
}

稍后当GUI事件导致classAInstance调用- (BOOL)runBlock;时 (它应该是)我得到以下崩溃堆栈:

    0 objc_exception_throw
    3 __forwarding_prep_0___
    4 __58-[ClassB applicationDidFinishLaunching:]_block_invoke_037
    5 -[NSBlockOperation main]
    11 start_wqthread

我得到的最后一个调试日志是:

    sender: __NSMallocBlock__
    -[__NSMallocBlock__ doSomething]: unrecognized selector sent to instance 0x10043f2a0

为什么该块的参数突然变成__NSMallocBlock__? 我显然已经传递了其他东西(即sender),不是吗?

4 个答案:

答案 0 :(得分:1)

看起来当你调用runBlock时,你传递一个块作为发送者。我能够很好地运行你的代码。 Matt Gallagher有一篇关于How blocks are implemented的文章,可以帮助您调试问题。

  

如果复制NSStackBlock,它将返回NSMallocBlock   (表示其更改的分配位置)。

答案 1 :(得分:1)

找到原因。呃,我现在觉得很蠢。

错误是通过以下方式将块(以及第二个)作为关联对象附加到我的对象:

static NSString * const FirstBlockKey;
static NSString * const SecondBlockKey;

objc_setAssociatedObject(self, FirstBlockKey, blockA, OBJC_ASSOCIATION_COPY);
objc_setAssociatedObject(self, SecondBlockKey, blockB, OBJC_ASSOCIATION_COPY);

虽然我应该清楚地使用它:

objc_setAssociatedObject(self, (void *)&FirstBlockKey, block, OBJC_ASSOCIATION_COPY);

FirstBlockKeySecondBlockKey显然都是0x0,而他们自己的指针却不是。{/ p>

这样它只是调用了错误的块(因为它们都分配给了相同的“0x0”键)。这些块具有不同的返回和参数类型,这似乎导致了传递的块参数的奇怪交换。现在工作正常。


话虽如此: Joe Ryan & wbyoung ,感谢您的努力!

答案 2 :(得分:0)

Joe的回答似乎很可能是正确的。你必须共享调用runBlock的代码,以便有人知道实际发生了什么,但是考虑到你提供的方法的名称,它可能看起来像:

[classAInstance runBlock:^BOOL(id sender) {
   // some code
}];

实际上会打印NSStackBlock(当然除非首先复制块,在这种情况下它将是NSMallocBlock)。

[classAInstance runBlock:@"Hello world"];

会导致输出:

sender: NSString
-[NSString doSomething]: unrecognized selector sent to instance

没有你的runBlock调用,虽然没有办法知道出错的地方。您也可能使用正确的参数调用方法,但是您有一个内存分配问题导致在您期望另一个对象存在的区域中分配块,但这可能不太可能。

答案 3 :(得分:-1)

这可能是你的问题:

NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];

你正在创建一个NSOperationQueue,然后自动释放它,然后期望它为你工作。如果你想在那里的一个单独的线程中异步工作,请使用带有并发队列的dispatch_async():

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
    NSLog(@"sender: %@", [sender class]);
    [sender doSomething];
});