首页 文章

“__block”关键字是什么意思?

提问于
浏览
410

Objective-C中的 __block 关键字究竟是什么意思?我知道它允许你修改块内的变量,但我想知道......

  • 它究竟是什么告诉编译器的?

  • 它还有什么用吗?

  • 如果就是这样的话那么为什么首先需要呢?

  • 它在文档中的任何地方吗? (我找不到) .

8 回答

  • 2

    __block 是一种存储类型,用于使范围变量可变,更坦率地说,如果使用此说明符声明变量,其引用将传递给块而不是只读副本以获取更多详细信息,请参阅Blocks Programming in iOS

  • 6

    @bbum在blog post中覆盖了深度块,并触及__block存储类型 .

    __block是一种独特的存储类型就像静态,自动和易失性一样,__block是一种存储类型 . 它告诉编译器变量的存储将以不同方式进行管理 . ...但是,对于__block变量,块不会保留 . 您可以根据需要保留和释放 . ...

    对于用例,您会发现 __block 有时用于避免保留周期,因为它不保留参数 . 一个常见的例子是使用self .

    //Now using myself inside a block will not 
    //retain the value therefore breaking a
    //possible retain cycle.
    __block id myself = self;
    
  • 8

    __block 是一个存储限定符,可以通过两种方式使用:

    • 标记变量存在于原始变量的词法范围与该范围内声明的任何块之间共享的存储中 . 并且clang将生成一个表示此变量的结构,并通过引用(而不是值)使用此结构 .

    • 在MRC中, __block 可用于避免块捕获保留对象变量 . 小心这对ARC不起作用 . 在ARC中,您应该使用 __weak .

    有关详细信息,请参阅apple doc .

  • 0

    通常,当您不使用__block时,该块将复制(保留)该变量,因此即使您修改了该变量,该块也可以访问旧对象 .

    NSString* str = @"hello";
    void (^theBlock)() = ^void() {
        NSLog(@"%@", str);
    };
    str = @"how are you";
    theBlock(); //prints @"hello"
    

    In these 2 cases you need __block:

    1.如果你想修改块内的变量并希望它在外面可见:

    __block NSString* str = @"hello";
    void (^theBlock)() = ^void() {
        str = @"how are you";
    };
    theBlock();
    NSLog(@"%@", str); //prints "how are you"
    

    2.如果要在声明块后修改变量,并且希望块看到更改:

    __block NSString* str = @"hello";
    void (^theBlock)() = ^void() {
        NSLog(@"%@", str);
    };
    str = @"how are you";
    theBlock(); //prints "how are you"
    
  • 2

    这意味着它是一个前缀的变量可以在一个块中使用 .

  • 494

    它告诉编译器在块中使用它时,必须以特殊方式处理由它标记的任何变量 . 通常,复制也使用块中使用的变量及其内容,因此对这些变量所做的任何修改都不会显示在块之外 . 当它们用 __block 标记时,在块内部进行的修改也在其外部可见 .

    有关示例和更多信息,请参阅Apple的Blocks Programming Topics中的The __block Storage Type .

    这个重要的例子是:

    extern NSInteger CounterGlobal;
    static NSInteger CounterStatic;
    
    {
        NSInteger localCounter = 42;
        __block char localCharacter;
    
        void (^aBlock)(void) = ^(void) {
            ++CounterGlobal;
            ++CounterStatic;
            CounterGlobal = localCounter; // localCounter fixed at block creation
            localCharacter = 'a'; // sets localCharacter in enclosing scope
        };
    
        ++localCounter; // unseen by the block
        localCharacter = 'b';
    
        aBlock(); // execute the block
        // localCharacter now 'a'
    }
    

    在此示例中,在调用块之前,都会修改 localCounterlocalCharacter . 但是,在块内部,由于 __block 关键字,只能对 localCharacter 进行修改 . 相反,该块可以修改 localCharacter ,并且此修改在块外部可见 .

  • 26

    希望对你有帮助

    假设我们有一个代码:

    {
         int stackVariable = 1;
    
         blockName = ^()
         {
          stackVariable++;
         }
    }
    

    它会给出一个错误,例如“变量不可赋值”,因为块内的堆栈变量默认是不可变的 .

    在声明之前添加__block(存储修饰符)使其在块内变为可变,即 __block int stackVariable=1;

  • 5

    来自Block Language Spec

    除了新的Block类型,我们还为局部变量引入了一个新的存储限定符__block . [testme:块文字中的__block声明] __block存储限定符与现有的本地存储限定符auto,register和static互斥 . [testme] __block限定的变量就像它们在分配的存储中一样,并且此存储是在最后一次使用所述变量后自动恢复 . 实现可以选择优化,其中存储最初是自动的,并且仅在引用块的Block_copy上“移动”到分配的(堆)存储 . 这些变量可能会像正常变量一样发生变异 . 在__block变量是块的情况下,必须假设__block变量驻留在分配的存储中,因此假定它引用也在分配的存储中的块(它是Block_copy操作的结果) . 尽管如此,如果实现为块提供初始自动存储,则没有规定执行Block_copy或Block_release . 这是由于潜在的几个线程试图更新共享变量的固有竞争条件以及处理旧值和复制新值的同步需求 . 这种同步超出了本语言规范的范围 .

    有关__block变量应编译到的内容的详细信息,请参阅Block Implementation Spec,第2.3节 .

相关问题