Подтвердить что ты не робот

Репликация Block_copy?

У меня есть код, который по существу сводится к следующему:

-(void)doSomethingWithBlock:(BlockTypedef)block
{
    [Foo doSomethingElseWithBlock:^() {
        block();
    }];
}

Foo doSomethingElseWithBlock: вызывает Block_copy и Block_release в блоке, который он получает. Нужно ли это также во внешнем пространстве, или внутренний Block_copy справится с этим?

4b9b3361

Ответ 1

Я цитирую Руководство по программированию на блоках на сайте документации разработчика Apple:

При копировании блока любые ссылки на другие блоки из этого блока копируются при необходимости - можно копировать все дерево (сверху). Если у вас есть переменные блока и вы ссылаетесь на блок из блока, этот блок будет скопирован.

Когда вы копируете блок на основе стека, вы получаете новый блок. Однако, если вы копируете блок на основе кучи, вы просто увеличиваете счетчик сохранения этого блока и возвращаете его в качестве возвращаемого значения функции или метода копирования.

Ответ 2

Да, это безопасно. Вам не нужно делать копию. В то время, когда -[Foo doSomethingElseWithBlock:] создает копию вашего литерального блока, он скопирует внутренний блок в кучу.

Я написал несколько тестовых кодов, чтобы доказать себе, что это происходит; см., как printer (используется только в block1) копируется из стека в кучу в момент вызова Block_copy(block2).

#include <Block.h>
#include <dispatch/dispatch.h>
#include <stdio.h>

typedef void (^void_block)();

class ScopedPrinter {
  public:
    ScopedPrinter() {
        printf("construct %p\n", this);
    }
    ScopedPrinter(const ScopedPrinter& other) {
        printf("copy %p <- %p\n", this, &other);
    }
    ~ScopedPrinter() {
        printf("destroy %p\n", this);
    }
};

void_block invoke(void_block other) {
    printf("other %p\n", (void*)other);
    void_block block2 = ^{
        printf("other %p\n", (void*)other);
        other();
    };
    printf("block2 created\n");
    block2 = Block_copy(block2);
    printf("block2 copied\n");
    return block2;
}

void_block make_block() {
    ScopedPrinter printer;
    printf("printer created\n");
    void_block block1 = ^{
        printf("block1 %p\n", &printer);
    };
    printf("block1 created\n");
    return invoke(block1);
}

int main() {
    void_block block = make_block();
    block();
    Block_release(block);
    return 0;
}

Стенограмма:

construct 0x7fff6a23fa70
printer created
copy 0x7fff6a23fa50 <- 0x7fff6a23fa70
block1 created
other 0x7fff6a23fa30
block2 created
copy 0x10a700970 <- 0x7fff6a23fa50
block2 copied
destroy 0x7fff6a23fa50
destroy 0x7fff6a23fa70
other 0x10a700950
block1 0x10a700970
destroy 0x10a700970

Ответ 3

Внутренний Block_copy() здесь не имеет особого значения. То, что вы хотите отслеживать, заключается в том, живет ли данный блок в стеке или в куче. Рассмотрим этот код на основе вашего примера:

@interface Foo : NSObject
@end

@implementation Foo

typedef void(^BlockTypedef)(void);

+(void)doSomethingElseWithBlock:(BlockTypedef)block
{
  NSLog(@"block=%@", block);
  BlockTypedef myBlock = Block_copy(block);
  NSLog(@"myBlock=%@", myBlock);
  myBlock();
  Block_release(myBlock);
}

+(void)doSomethingWithBlock:(BlockTypedef)block
{
  [Foo doSomethingElseWithBlock:^() {
    block();
  }];
}
@end

int main (int argc, const char * argv[])
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  int i = 3;
  BlockTypedef block = ^{ printf("i=%d\n", i); };
  NSLog(@"block=%@", block);
  [Foo doSomethingWithBlock:block];
  block();
  NSLog(@"block=%@", block);
  [pool drain];
  return 0;
}

Это должно быть хорошо, но block и myblock - это разные блоки. block является блоком стека и имеет область вызова вызывающего стека. Он будет существовать до выхода main(). myblock является блоком malloc (кучи) и будет существовать до его освобождения. Вам нужно убедиться, что вы не пытаетесь взять не скопированную ссылку на block и использовать ее после завершения стека. Вы не можете вставить block в ivar, не копируя его.

Иоахим Бенгтсон имеет лучшую рецензию, о которой я знаю. @bbum также написал об этом. (Если ббум блуждает здесь и говорит, что я идиот об этом, тогда слушай его, но я думаю, что я здесь.)