如何在Objective-C块中编写代码来引用block对象本身?

时间:2021-07-19 13:29:52

self is merely a captured variable inside a block and doesn't reference the block itself, so how does a block reference itself without having an explicit captured variable for that purpose?

self仅仅是块中的捕获变量,不引用块本身,那么块如何在没有显式捕获变量的情况下引用本身呢?

5 个解决方案

#1


16  

__block void(^strawberryFields)();
strawberryFields = [^{ strawberryFields(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
               strawberryFields);
  • you use the __block because the block will make a copy of the value of strawberryFields when the block is created which will be before the assignment.

    您使用__block,因为在创建block时,块将在赋值之前生成一个strawberryFields的值的副本。

  • you also must copy the block prior to any other copy operation or else you'll end up with a block that references the on-stack original version.

    您还必须在任何其他复制操作之前复制该块,否则您将得到引用堆栈上原始版本的块。

  • note that the above code leaks the block. Somewhere, there needs to be a release of that block to balance the copy.

    注意,上面的代码泄漏了块。在某些地方,需要发布该块以平衡副本。

#2


5  

I found this pattern to work and stable for ARC (automatic reference counting), both in Debug and Release builds.

我发现这个模式对于ARC(自动引用计数)来说是有效且稳定的,在调试和发布版本中都是如此。

-(void) someMethod
{
    // declare a __block variable to use inside the block itself for its recursive phase.
    void __block (^myBlock_recurse)();

    // define the block
    void (^myBlock)() = ^{
        // ... do stuff ...
        myBlock_recurse(); // looks like calling another block, but not really.
    };

    // kickstart the block
    myBlock_recurse = myBlock; // initialize the alias
    myBlock(); // starts the block
}

Initially I tried just putting a __block modifier to myBlock and use that variable directly to recurse within the block's implementation. That works on the ARC Debug build but breaks with an EXC_BAD_ACCESS on the Release build. On the other hand removing the __block modifier raises a "variable not defined when captured by block" warning (and I was reluctant to run it and test).

最初,我尝试将__block修饰符放到myBlock中,并使用该变量直接在block的实现中递归。它可以在ARC Debug构建上工作,但会在发布构建上中断EXC_BAD_ACCESS。另一方面,删除__block修饰符会引发“被block捕获时未定义的变量”警告(我不愿意运行它并进行测试)。

#3


0  

I have never tried this before and not 100% sure it's useful, if valid, but for example:

我以前从来没有尝试过,也不是100%确定它是否有用,如果有效的话,但是例如:

typedef void (^BasicBlock)(void);

__block BasicBlock testBlock;
testBlock  = ^{NSLog(@"Testing %p", &testBlock);};
testBlock();

You probably have declare the variable with __block to prevent self-retain cycle.

您可能已经声明了带有__block的变量,以防止自保留循环。

#4


0  

The block needs some way to nil out its own reference. Typically it is done by storing the block in a property of the class.

块需要某种方法来空出它自己的引用。通常,它是通过将块存储在类的属性中来实现的。

Sometimes you can prefer to not use a property. Here is how you do it without a property:

有时你可以不使用属性。以下是你在没有财产的情况下如何做的:

    __weak id weakSelf = self;
    __block id block = ^{

        if(weakSelf) {

            // .. do whatever

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);
        }
        else {

            block = nil;
        }
    };

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);

The key thing to keep in mind is that all code paths must lead to a block = nil. We do that here by calling the block every 5 seconds until weakSelf turns nil.

要记住的关键一点是,所有代码路径都必须导致块= nil。在这里,我们每隔5秒调用block,直到weakSelf变成nil。

#5


0  

Note that in ARC, it's a little different -- __block object pointer variables are by default retained in ARC, unlike in MRC. Thus, it will cause a retain cycle. It is necessary for the block to capture a weak reference to itself (using __weak) in order to not have a retain cycle.

注意,在ARC中,它有一点不同——__block对象指针变量默认保留在ARC中,不像在MRC中。因此,它将导致一个保留周期。块需要捕获对自身的弱引用(使用__weak),以避免保留循环。

However, we still need a strong reference to the block somewhere. If there are no strong references, the block (which is on the heap since it's copied) will be deallocated. Thus, we need two variables, one strong and one weak, and inside the block use the weak one to reference itself:

但是,我们仍然需要对某个块进行强引用。如果没有强引用,块(由于被复制而在堆上)将被重新分配。因此,我们需要两个变量,一个强的和一个弱的,在块内部使用弱的来引用它自己:

__block __weak void(^weakBlock)();
void(^myBlock)();
weakBlock = myBlock = [^{ weakBlock(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
               myBlock);

#1


16  

__block void(^strawberryFields)();
strawberryFields = [^{ strawberryFields(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
               strawberryFields);
  • you use the __block because the block will make a copy of the value of strawberryFields when the block is created which will be before the assignment.

    您使用__block,因为在创建block时,块将在赋值之前生成一个strawberryFields的值的副本。

  • you also must copy the block prior to any other copy operation or else you'll end up with a block that references the on-stack original version.

    您还必须在任何其他复制操作之前复制该块,否则您将得到引用堆栈上原始版本的块。

  • note that the above code leaks the block. Somewhere, there needs to be a release of that block to balance the copy.

    注意,上面的代码泄漏了块。在某些地方,需要发布该块以平衡副本。

#2


5  

I found this pattern to work and stable for ARC (automatic reference counting), both in Debug and Release builds.

我发现这个模式对于ARC(自动引用计数)来说是有效且稳定的,在调试和发布版本中都是如此。

-(void) someMethod
{
    // declare a __block variable to use inside the block itself for its recursive phase.
    void __block (^myBlock_recurse)();

    // define the block
    void (^myBlock)() = ^{
        // ... do stuff ...
        myBlock_recurse(); // looks like calling another block, but not really.
    };

    // kickstart the block
    myBlock_recurse = myBlock; // initialize the alias
    myBlock(); // starts the block
}

Initially I tried just putting a __block modifier to myBlock and use that variable directly to recurse within the block's implementation. That works on the ARC Debug build but breaks with an EXC_BAD_ACCESS on the Release build. On the other hand removing the __block modifier raises a "variable not defined when captured by block" warning (and I was reluctant to run it and test).

最初,我尝试将__block修饰符放到myBlock中,并使用该变量直接在block的实现中递归。它可以在ARC Debug构建上工作,但会在发布构建上中断EXC_BAD_ACCESS。另一方面,删除__block修饰符会引发“被block捕获时未定义的变量”警告(我不愿意运行它并进行测试)。

#3


0  

I have never tried this before and not 100% sure it's useful, if valid, but for example:

我以前从来没有尝试过,也不是100%确定它是否有用,如果有效的话,但是例如:

typedef void (^BasicBlock)(void);

__block BasicBlock testBlock;
testBlock  = ^{NSLog(@"Testing %p", &testBlock);};
testBlock();

You probably have declare the variable with __block to prevent self-retain cycle.

您可能已经声明了带有__block的变量,以防止自保留循环。

#4


0  

The block needs some way to nil out its own reference. Typically it is done by storing the block in a property of the class.

块需要某种方法来空出它自己的引用。通常,它是通过将块存储在类的属性中来实现的。

Sometimes you can prefer to not use a property. Here is how you do it without a property:

有时你可以不使用属性。以下是你在没有财产的情况下如何做的:

    __weak id weakSelf = self;
    __block id block = ^{

        if(weakSelf) {

            // .. do whatever

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);
        }
        else {

            block = nil;
        }
    };

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);

The key thing to keep in mind is that all code paths must lead to a block = nil. We do that here by calling the block every 5 seconds until weakSelf turns nil.

要记住的关键一点是,所有代码路径都必须导致块= nil。在这里,我们每隔5秒调用block,直到weakSelf变成nil。

#5


0  

Note that in ARC, it's a little different -- __block object pointer variables are by default retained in ARC, unlike in MRC. Thus, it will cause a retain cycle. It is necessary for the block to capture a weak reference to itself (using __weak) in order to not have a retain cycle.

注意,在ARC中,它有一点不同——__block对象指针变量默认保留在ARC中,不像在MRC中。因此,它将导致一个保留周期。块需要捕获对自身的弱引用(使用__weak),以避免保留循环。

However, we still need a strong reference to the block somewhere. If there are no strong references, the block (which is on the heap since it's copied) will be deallocated. Thus, we need two variables, one strong and one weak, and inside the block use the weak one to reference itself:

但是,我们仍然需要对某个块进行强引用。如果没有强引用,块(由于被复制而在堆上)将被重新分配。因此,我们需要两个变量,一个强的和一个弱的,在块内部使用弱的来引用它自己:

__block __weak void(^weakBlock)();
void(^myBlock)();
weakBlock = myBlock = [^{ weakBlock(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
               myBlock);