Objective-C: 初始化存储在集合中的块

Objective-C: initialization of blocks stored in collections

我读了这篇文章:Storing Blocks in an Array 并想澄清一些时刻。

考虑下一个代码:

NSMutableArray* storage1;
NSMutableArray* storage2;
-(id)init{
    self=[super init];
    if(self){
        storage1=[NSMutableArray new];
        storage2=[NSMutableArray new];
    }
return self;
}
-(void) f1{
   __block int x=1;

  dispatch_block_t b0=^{
     i++;
     i++;
     sleep_indefinitely
  };
  [storage1 addObject:b0];
  dispatch_async(global_concurrent_thread,b0());
  [storage2 addObject:[storage1 objectAtIndex:0]];
}

问题是:storage2 是否会包含 updated 值为 i 的块?如果我想拥有 2 个独立的实例,是否有必要在将其存储在集合中之前复制块?如果是,那么在复制过程中如何初始化带有 __block 说明符的变量?

首先,storage1和storage2中的block store是同一个block instance。然后,storage2 中的块存储可以更新 i 变量。

其次,如果你想有2个独立的块实例。您必须调用 Block_copy 函数。

第三,当你复制块时,带有__block说明符的变量在两个块中是相同的。无法使用 __block 说明符复制变量。 最后,如果你也复制块,2块将可以更新变量i

根据 Apple 文档:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW6

__block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or created within the variable’s lexical scope. Thus, the storage will survive the destruction of the stack frame if any copies of the blocks declared within the frame survive beyond the end of the frame (for example, by being enqueued somewhere for later execution). Multiple blocks in a given lexical scope can simultaneously use a shared variable.

您的问题的答案在于理解 lifetime – 程序中创建的每个变量和对象都有一个生命周期,它决定了变量或对象的存活时间。 __block 属性修改了它所应用的变量的生命周期,可能会增加它。

关于血淋淋的细节(会有变量和对象的死尸乱扔答案;-))从一个简单函数中声明的变量开始:

int answer1(void)
{
   int x = 21;    // variable x is created with a fixed lifetime of the end of the function
                  // invocation – each function invocation creates a new distinct variable x
   return x * 2;
   // end of the function, x dies
}

answer1() returns 42 的调用。在该调用期间,一个变量被创建和销毁,调用后它不再存在。每次调用 answer1() 都会创建一个新的不同变量,然后在调用 returns.

时销毁它

标准局部变量的生命周期与创建它们的函数、方法或块语句有关。

动态创建的对象的生命周期不受 function/method/block 创建它们的语句的限制,但只要存在对它们的强引用就一直存在。

int answer2(void)
{
   NSMutableArray *x = [NSMutableArray new]; // variable x is created with a fixed lifetime
                                             // of the end of the function
                                             // an NSMutableArray object is created with an
                                             // indeterminate lifetime – it will live as long
                                             // as there exists a strong refernece to it
                                             // a strong reference to this object is stored in x
   [x addObject:@21];
   return [x[0] intValue] * 2;
   // end of the function, x dies
   // as x contained the only strong reference to the array object
   // so that object is now longer wanted and can now be culled
   // – its lifetime is now over as well
}

answer2() 的调用也 returns 42。重要提示:调用期间创建的数组对象已死亡,不是因为调用已返回,变量 x 死亡的原因,而是因为不再有任何强引用存储在任何地方 - 不需要它被剔除。

现在让我们看看块。创建时,块对象包含它引用的任何局部变量中值的 个副本

typedef int (^IntBlock)(void);   // for convenience

int answer3(void)
{
   int x = 20;    // variable x is created with a fixed lifetime of the end of the function

   IntBlock b1 = ^{ return x; }; // variable b1 is created with a fixed lifetime of the end
                                 // of the function
                                 // a block object is created with an indeterminate lifetime
                                 // and a strong reference to it is stored in b1
                                 // the block object contains a copy of the *value* in x,
                                 // i.e. 20

   x += 2;

   IntBlock b2 = ^{ return x; }; // variable b2 is created with a fixed lifetime of the end
                                 // of the function
                                 // a block object is created with an indeterminate lifetime
                                 // and a strong reference to it is stored in b1
                                 // the block object contains a copy of the *value* in x,
                                 // i.e. 22

   return b1() + b2();
   // end of function
   // x, b1 and b2 all die
   // as b1 & b2 contained the only strong references to the two block objects
   // they can now be culled – their lifetimes are over
}

answer3() returns 42 的调用(真是惊喜 ;-))。在调用 answer3() 期间创建了两个不同的块,尽管代码主体相同,但它们包含 x.

不同

最后我们来到 __block,局部变量的生命周期增强属性,任何在出生时拥有此属性的局部变量都不会在其创建 function/method/block 语句结束时死亡:

typedef void (^VoidBlock)(void);

IntBlock answer4(void)
{
   __block int x = 42;  // variable x is created with a lifetime the longer of:
                        //  * the lifetime of the current invocation of answer4()
                        //  * the lifetime of the longest living block which
                        //    uses x

   VoidBlock b1 = ^{ x = x / 2; };     // variable b1 is created with a fixed lifetime of the end
                                       // of the function
                                       // a block object is created with an indeterminate lifetime
                                       // and a strong reference to it is stored in b1
                                       // the block object contains a reference to the *variable* in x

   IntBlock b2 = ^{ return x * 2; };   // variable b2 is created with a fixed lifetime of the end
                                       // of the function
                                       // a block object is created with an indeterminate lifetime
                                       // and a strong reference to it is stored in b1
                                       // the block object contains a reference to the *variable* in x
   b1();          // call b1(), alters the value in x
   return b2;     // return a reference to the second block
   // end of function
   // b1 dies
   // as b1 contained the only strong reference to the first block it can now be culled
   // b2 also dies
   // however a reference to the block it referenced is returned by the function, so
   // that block lives
   // the returned block references x so it to lives
}

void test4(void)
{
   IntBlock b1 = answer4();            // a reference to a block is returned by answer4() and stored in b1
   NSLog(@"The answer is %d", b1());   // outputs 42
   // end of function
   // b1 dies
   // as b1 contained the onlyt surviving reference to the block returned by answer4()
   // that block may now be culled
   // as that block contains the only surviving reference to the variable x
   // that variable may now be culled
}

调用 test4() 输出 The answer is 42。请注意 b1b2.

如何只共享一个 x

进一步注意局部变量 x 的生命周期如何延长到 answer4() 的调用之后,因为它被返回的块对象捕获。然而,一旦块对象的时间到了,x 就会被剔除——就像一个对象一样,它的生命周期取决于对它感兴趣的东西。

x 的生命周期条件与对象的条件非常相似是巧合吗?不,下一个示例是 effectively(即精确细节会有所不同,可见行为是相同的)编译器如何处理 answer4():

@interface LifetimeExtenderObject5 : NSObject
@property int x;
@end
@implementation LifetimeExtenderObject5
@end

IntBlock answer5(void)
{
   LifetimeExtenderObject5 *leo = [LifetimeExtenderObject5 new];  // variable leo is created with a lifetime of
                                                                  // the end of the function
                                                                  // a LifetimeExtenderObject5 is created with
                                                                  // an indeterminate lifetime and a reference
                                                                  // to it stored in leo
   leo.x = 42;

   VoidBlock b1 = ^{ leo.x = leo.x / 2; };      // variable b1 is created with a fixed lifetime of the end
                                                // of the function
                                                // a block object is created with an indeterminate lifetime
                                                // and a strong reference to it is stored in b1
                                                // the block object contains a copy of the *value* in leo
                                                // this value is a strong reference to the created
                                                // LifetimeExtenderObject5, so now there are two strong
                                                // references to that object

   IntBlock b2 = ^{ return leo.x * 2; };        // variable b2 is created with a fixed lifetime of the end
                                                // of the function
                                                // a block object is created with an indeterminate lifetime
                                                // and a strong reference to it is stored in b1
                                                // the block object contains a copy of the *value* in leo
                                                // this value is a strong reference to the created
                                                // LifetimeExtenderObject5, so now there are three strong
                                                // references to that object
   b1();          // call b1(), alters the value in x
   return b2;     // return a reference to the second block
   // end of function
   // leo dies, but the LifetimeExtenderObject5 object it references has other strong
   // references so it lives
   // b1 dies
   // as b1 contained the only strong reference to the first block it can now be culled
   // that block contained a string reference to created LifetimeExtenderObject5 object,
   // but there are still remaining strong references to that object so it lives
   // b2 also dies
   // however a reference to the block it referenced is returned by the function, so
   // that block lives
   // that block contains a strong reference, the last one, to the created
   // LifetimeExtenderObject5 object, so it still lives.
}

void test5(void)
{
   IntBlock b1 = answer5();            // a reference to a block is returned by answer5() and stored in b1
   NSLog(@"The answer is %d", b1());   // outputs 42
   // end of function
   // b1 dies
   // as b1 contained the only surviving reference to the block returned by answer5()
   // that block may now be culled
   // as that block contains the only surviving reference to the created
   // LifetimeExtenderObject5 object that object may now be culled
}

您问的是:

Is it necessary to copy block before storing it in collection if I want to have 2 independent instances?

以上希望告诉您,复制不会让您获得捕获的 __block 变量的两个独立实例。为此,您需要 distinct __block 变量来捕获,这些变量是从声明它们的函数的不同调用中获得的。每次调用 answer4() 以上 returns 已捕获 __block 属性变量 x.

的不同/"independent instance" 的块

HTH 令人困惑!

首先,在您的代码中,每次调用 f1 只创建一个块实例,因为只有一个 ^{ } 表达式,它只有 运行 一次。即使在 f1 的特定调用中创建了多个块,它们也会引用相同的 x 变量,因为它是一个 __block 变量,所以它是通过引用捕获的。此外,复制块不会创建块的多个实例——如果块不在堆上,它只是将块从堆栈移动到堆。

有必要在将块存储到集合之前复制块,不是因为它会创建块的多个实例(它不会),而是因为块默认情况下,作为优化从堆栈开始。如果你将它存储在一个比创建块的范围更长的地方,你必须 "copy" 将它从堆栈移动到堆的块(如果它不在那里)。 (该集合将 "retain" 它需要存储的对象,但保留一个块不会将其从堆栈移动到堆。)但是,最新版本的 Clang 编译器在 ARC 中编译时,将隐式当你将一个块类型的变量传递给一个非块对象类型的函数参数时添加一个副本(例如这里,-[NSMutableArray addObject:],它采用 id 类型),所以你不必这样做你自己。