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



我完全被这个问题难住了,希望有人能帮助我:

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事件导致classinstance调用- (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

为什么block的参数突然变成了__NSMallocBlock__呢?我显然是传递其他东西(即sender)给它,不是吗?

看起来当你调用runBlock时,你正在传递一个块作为发送者。我可以很好地运行你的代码。Matt Gallagher有一篇关于如何实现块的文章,可以帮助您调试问题。

如果你复制一个NSStackBlock,它会返回一个nssmalllocblock(表示已更改的分配位置)。

找到原因了。啊,我现在觉得自己很蠢。

错误是将块(以及第二个块)作为关联对象通过:

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,而它们自己的指针却不是。

这样它就调用了错误的块(因为它们都被分配到相同的' 0x0'键)。块具有不同的返回和参数类型,这似乎导致了传递块参数的奇怪交换。


,说:, 瑞安,wbyoung,谢谢大家的努力!

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调用,虽然没有办法知道哪里出了问题。也有可能你用正确的参数调用方法,但是你有一个内存分配问题,导致一个块被分配在你期望另一个对象存在的区域,但这可能不太可能。

这可能是你的问题:

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];
});

最新更新