Странный сбой при обмене аргументами блочных и фантомных блоков - PullRequest
3 голосов
/ 20 июля 2011

Я полностью озадачен этим и надеюсь, что кто-нибудь может мне здесь помочь:

Класс 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 ]

1 голос
/ 21 июля 2011

Нашел причину. Я чувствую себя глупо сейчас.

Ошибка заключалась в прикреплении блока (как и второго) к моему объекту в качестве связанных объектов через:

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

FirstBlockKey и SecondBlockKey оба, очевидно, 0x0, в то время как их собственные указатели - нет.

Таким образом, он просто вызвал неправильный блок (поскольку они оба были назначены на одну и ту же клавишу `0x0 '). Блоки имели разные типы возвращаемого значения и аргумента, что, по-видимому, вызвало странный обмен переданными аргументами блока. Работает нормально сейчас.


Как говорится: Джо , Райан & wbyoung , спасибо за ваши усилия, ребята!

1 голос
/ 20 июля 2011

Похоже, что когда вы вызываете runBlock, вы передаете блок в качестве отправителя.Я смог запустить ваш код просто отлично. Мэтт Галлахер имеет статью о Как реализованы блоки , которые могут помочь вам отладить вашу проблему.

Если вы скопируете NSStackBlock, он вернет NSMallocBlock (указывая его измененное расположение выделения).

0 голосов
/ 20 июля 2011

Это, вероятно, ваша проблема:

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];
});
0 голосов
/ 20 июля 2011

Ответ Джо кажется почти правдоподобным. Вам также придется поделиться кодом, который вызывает runBlock, чтобы кто-то знал, что на самом деле происходит, но, учитывая имя предоставленного вами метода, он, вероятно, выглядит примерно так:

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

Который на самом деле будет печатать NSStackBlock вместо этого (если, конечно, блок не был скопирован первым, в этом случае это будет NSMallocBlock).

[classAInstance runBlock:@"Hello world"];

Результатом будет:

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

Без вашего вызова runBlock, хотя нет никакого способа узнать, где что-то пошло не так. Также возможно, что вы вызываете метод с правильным аргументом, но у вас есть проблема выделения памяти, которая приводит к выделению блока в той области, где вы ожидаете существование другого объекта, но это, вероятно, менее вероятно.

...