Blocks Programming Topics

Blocks Programming Topicshtml

Block对象是C语法的, 且是一个运行时特性, 它们相似于标准C函数, 可是除了可执行代码外它们还可能包含到自动(栈)或托管(堆)内存的变量绑定, 所以, 一个block能够维护一组状态(数据), 当执行Block时能够用来影响执行行为c++

可使用block构成能够传递给API的函数表达式(block能够作接口参数), 用于存储数据, 能够在多线程中使用, block作为回调特别有用, 由于block既包含要在回调上执行的代码,又包含执行期间所需的数据编程

OS X v10.6 Xcode开发人员工具附带提供了GCC和Clang中的block, 能够在OS X v10.6和更高版本以及iOS 4.0和更高版本中使用block, block运行时是开源的能够在 LLVM’s compiler-rt subproject repository中找到. Block也做为 N1370: Apple’s Extensions to C已提交给C标准工做组, 因为Objective-C和C++都从C派生而来, 所以block被设计为可用于全部三种语言(以及Objective-C++)数组

Getting Started with Blocks

Declaring and Using a Block

使用^运算符声明一个block变量并指示block文字的开头, block的主体自己包含在{}安全

int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};
复制代码

Blocks

注意block可以使用其定义范围内的变量bash

若是将block声明为变量则能够像使用函数同样使用它数据结构

int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};
 
printf("%d", myBlock(3));
// prints "21"
复制代码

Using a Block Directly

在不少状况下, 不须要声明block变量, 取而代之的是只需在须要将其做为参数的位置编写内联代码block, 示例使用qsort_b函数, qsort_b与标准qsort_r函数类似可是将一个block做为其一个参数多线程

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
 
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    return strncmp(left, right, 1);
});
 
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
复制代码

Blocks with Cocoa

Cocoa框架中的几种方法都以一个block做为参数, 一般作对对象集合执行操做,以及在操做完成后用做回调, 如下示例显示如何将block与NSArray方法sortedArrayUsingComparator:一块儿使用闭包

NSArray *stringsArray = @[ @"string 1",
                           @"String 21",
                           @"string 12",
                           @"String 11",
                           @"String 02" ];
 
static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
        NSWidthInsensitiveSearch | NSForcedOrderingSearch;
NSLocale *currentLocale = [NSLocale currentLocale];
 
NSComparator finderSortBlock = ^(id string1, id string2) {
 
    NSRange string1Range = NSMakeRange(0, [string1 length]);
    return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
};
 
NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];
NSLog(@"finderSortArray: %@", finderSortArray);
 
/* Output: finderSortArray: ( "string 1", "String 02", "String 11", "string 12", "String 21" ) */
复制代码

__block Variables

block的强大功能是它能够在相同的词法范围内修改变量, 使用__block存储类型修饰符表示block能够修改变量, 沿用以前的示例, 可使用一个block变量来计算比较相等的字符串数, 在这种状况下, 将直接使用该block, 并将currentLocale用做该block内的只读变量app

NSArray *stringsArray = @[ @"string 1",
                          @"String 21", // <-
                          @"string 12",
                          @"String 11",
                          @"Strîng 21", // <-
                          @"Striñg 21", // <-
                          @"String 02" ];
 
NSLocale *currentLocale = [NSLocale currentLocale];
__block NSUInteger orderedSameCount = 0;
 
NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {
 
    NSRange string1Range = NSMakeRange(0, [string1 length]);
    NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];
 
    if (comparisonResult == NSOrderedSame) {
        orderedSameCount++;
    }
    return comparisonResult;
}];
 
NSLog(@"diacriticInsensitiveSortArray: %@", diacriticInsensitiveSortArray);
NSLog(@"orderedSameCount: %d", orderedSameCount);
 
/* Output: diacriticInsensitiveSortArray: ( "String 02", "string 1", "String 11", "string 12", "String 21", "Str\U00eeng 21", "Stri\U00f1g 21" ) orderedSameCount: 2 */
复制代码

Conceptual Overview

block对象提供了一种方法能够建立临时函数体, 以C以及基于C的语言(如Objective-C和C ++)的表达式进行建立. 在其余语言和环境中, block对象有时也称为"闭包(closure)"

Block Functionality

block是匿名内联代码的集合

  • 具备像函数同样的类型化参数列表
  • 具备推断或声明的返回类型
  • 能够从定义它的词汇范围(lexical scope)中捕获状态
  • 能够有选择地修改词法范围(lexical scope)的状态
  • 能够与相同词汇范围内定义的其余block共享修改的变量
  • 在销毁了词法范围(栈空间)后能够继续共享和修改在词法范围(栈空间)中定义的状态

能够复制一个block,并且还能够将其传递给其余线程以推迟执行(或在其本身的线程中传递给运行循环), 编译器和运行时能够确保block所引用的全部变量的生命周期, 尽管block可用于纯C和C++可是block也始终是Objective-C对象

Usage

block一般表明小的独立代码段. 所以它们特别有用, 能够封装能够同时执行的工做单元, 也能够封装集合中的项, 也能够封装其余操做完成后的回调

block能够做为传统的回调方案的替代有两个重要缘由

  • 它们容许在调用点处编写代码, 该代码稍后在方法实现的上下文中执行, 所以block一般是框架中方法经常使用的参数
  • 它们容许访问局部变量

Declaring and Creating Blocks

Declaring a Block Reference

block变量保存了对block对象的引用, 使用相似于声明函数指针的语法来声明它们, 固然不一样处是使用^而不是*, block类型能够与C类型系统的其他部分彻底相互操做

void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
复制代码

block还支持可变参数(...), 不带参数的block必须在参数列表中指定void

block被设计为彻底类型安全的, 经过为编译器提供一整套元数据以验证block的使用, 整套数据包含传递给block的参数以及返回值的分配. 能够将block引用强制转换为任意类型的指针, 反之亦然

可是不能经过指针引用运算符*取消对block的引用,由于没法在编译时计算block的大小

能够为block建立类型, 这样就能够在多个地方使用

typedef float (^MyBlockType)(float, float);
 
MyBlockType myFirstBlock = // ... ;
MyBlockType mySecondBlock = // ... ;
复制代码

Creating a Block

可使用^运算符来指示block文字表达式的开始, 它后面能够跟( )中包含的参数列表, block的主体包含在{}

示例

float (^oneFrom)(float);
 
oneFrom = ^(float aFloat) {
    float result = aFloat - 1.0;
    return result;
};
复制代码

若是未明确声明block表达式的返回值, 它能够从block的内容中自动推断出来, 若是推断出返回类型与参数列表都为空void, 那么也能够省略void参数列表, 若是存在多个return语句则它们必须彻底匹配(必要时使用强制类型转换)

Global Blocks

在文件级别能够将block用做全局变量

#import <stdio.h>
 
int GlobalInt = 0;
int (^getGlobalInt)(void) = ^{ return GlobalInt; };
复制代码

Blocks and Variables

Types of Variable

在block对象的代码体内,能够用五种不一样方式来处理变量

能够引用三种标准类型的变量,就像在函数中同样

  • 全局变量,包括静态局部变量
  • 全局函数(从技术上讲不是可变的)
  • 来自封闭范围的局部变量和参数

block还支持其余两种类型的变量

  • 在函数级别上的是__block变量, 它们在block(和封闭范围)内是可变的, 若是将任何引用block复制到堆中, 则将保留它们
  • const常量

最后,在方法实现中,block能够引用Objective-C实例变量

在block中使用的变量的规则

  • 全局变量是可访问的, 包括包含在词汇范围内的静态变量

  • 传递给block的参数是可访问的(就像函数的参数同样)

  • 处于封闭范围内的非静态局部变量被block捕获为const不可变的常量, 不可变

    • 程序中block表达式的位置会获取他们的值
    • 在嵌套block中从最近的封闭范围捕获值
  • __block存储修饰符声明的包围词法做用域的局部变量, 是可变的, 能够被修改

    • 任何更改都影响在词法范围内的关联的内容, 包括在同一封闭词法范围内定义的任何其余block
  • 在block的词法范围内声明的局部变量, 行为等同函数中的局部变量

使用局部非静态变量

int x = 123;
 
void (^printXAndY)(int) = ^(int y) {
 
    printf("%d %d\n", x, y);
};
 
printXAndY(456); // prints: 123 456
复制代码

尝试在block内为x分配新值将致使错误

int x = 123;
 
void (^printXAndY)(int) = ^(int y) {
 
    x = x + y; // error
    printf("%d %d\n", x, y);
};
复制代码

The __block Storage Type

经过应用__block存储类型修饰符, 能够指定导入的变量是可变的(便可读写), __block对于局变量相似于register, auto, static这些存储类型, 可是和它们是互斥的

__block变量存储在变量的词法范围与在该变量的词法范围内声明或建立的全部block和block副本之间共享的存储中, 所以, 在栈内声明的block的任何拷贝都会在在栈的尾都存活(例如,经过排队到某个地方以供之后执行), 则该存储将避免栈的销毁(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). 这句理解有点难搞), 给定词法范围内的多个block能够同时使用共享变量

做为一个最优项, block通常都是用在栈中, 若是block使用Block_copy拷贝(或者Objective-C 中发送了copy消息), 拷贝的内容就在上了, 因此, __block修饰的变量的地址能够随时间变化

__block变量还有两个限制

  • 它们不能是可变长度数组
  • 不能是包含C99可变长度数组的结构

__block示例

__block int x = 123; // x lives in block storage
 
void (^printXAndY)(int) = ^(int y) {
 
    x = x + y;
    printf("%d %d\n", x, y);
};
printXAndY(456); // prints: 579 456
// x is now 579
复制代码

如下示例显示了具备多种类型变量的block

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'
}
复制代码

Object and Block Variables

block做为变量能够提供对Objective-C和C++对象以及其余块的支持

Objective-C Objects

复制一个block时, 它将建立对该块内使用的对象变量的强引用, 若是在实现方法内使用block

  • 若是经过引用访问实例变量,则会对self进行强引用
  • **若是按值访问实例变量,则会对该变量进行强引用

如下示例说明了两种不一样的状况

dispatch_async(queue, ^{
    // instanceVariable is used by reference, a strong reference is made to self
    doSomethingWithObject(instanceVariable);
});
 
 
id localVariable = instanceVariable;
dispatch_async(queue, ^{
    /* localVariable is used by value, a strong reference is made to localVariable (and not to self). */
    doSomethingWithObject(localVariable);
});
复制代码

C++ Objects

一般能够在一个block中使用C++对象, 在成员函数中, 对成员变量和函数的引用是经过隐式导入的this指针进行的, 所以看起来是可变的, 若是拷贝block则有两个注意事项

  • 若是有一个__block存储类型的类来处理基于栈的C++对象, 一般复制构造函数将会被使用
  • 若是您在一个block中使用任何其余基于C++栈的对象, 它必须具备const复制构造函数, 而后使用该构造函数复制C++对象

Blocks

当拷贝一个block, 若有必要将复制该block中对其余block的任何引用, 若是有一个block变量将被复制, 并且这个block还引用了其余block, 那么引用的那个block也会被复制

Using Blocks

Invoking a Block

若是将块声明为变量则能够像使用函数同样使用它, 见示例

int (^oneFrom)(int) = ^(int anInt) {
    return anInt - 1;
};
 
printf("1 from 10 is %d", oneFrom(10));
// Prints "1 from 10 is 9"
 
float (^distanceTraveled)(float, float, float) =
                         ^(float startingSpeed, float acceleration, float time) {
 
    float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
    return distance;
};
 
float howFar = distanceTraveled(0.0, 9.8, 1.0);
// howFar = 4.9
复制代码

一般将块做为参数传递给函数或方法, 在这种状况下, 一般会建立一个内联block

Using a Block as a Function Argument

在不少状况下, 不须要声明block变量, 取而代之的是只需在须要将其做为参数的位置编写内联代码block, 示例使用qsort_b函数, qsort_b与标准qsort_r函数类似可是将一个block做为其一个参数

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
 
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    return strncmp(left, right, 1);
});
 
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
复制代码

下面的例子dispatch_apply函数所使用的的block

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));

#include <dispatch/dispatch.h>
size_t count = 10;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 
dispatch_apply(count, queue, ^(size_t i) {
    printf("%u\n", i);
});
复制代码

Using a Block as a Method Argument

Cocoa提供了许多使用block的方法, 与其余任何参数同样可将block做为方法参数传递

NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
 
BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);
 
test = ^(id obj, NSUInteger idx, BOOL *stop) {
 
    if (idx < 5) {
        if ([filterSet containsObject: obj]) {
            return YES;
        }
    }
    return NO;
};
 
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
 
NSLog(@"indexes: %@", indexes);
 
/* Output: indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)] */

//============================================================================

__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";
 
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];
 
// At this point, found == YES
复制代码

Copying Blocks

一般不须要复制(或保留)block, 仅当但愿该block在声明了该块的做用域销毁以后被使用时, 才须要进行复制, 复制的block会在堆上产生

可使用C函数复制和释放block

Block_copy();
Block_release();
复制代码

为避免内存泄漏须始终将Block_copy()Block_release()保持平衡

Patterns to Avoid

A block literal (that is, ^{ ... }) is the address of a stack-local data structure that represents the block. The scope of the stack-local data structure is therefore the enclosing compound statement, so you should avoid the patterns shown in the following examples:

block文字(即^ {...})是表示该block的堆栈本地数据结构的地址, 所以栈局部数据结构的范围是封闭的复合声明, 所以应该避免如下示例中显示的模式(翻的不通顺, 请看原句, 结合代码理解)

void dontDoThis() {
    void (^blockArray[3])(void);  // an array of 3 block references
 
    for (int i = 0; i < 3; ++i) {
        blockArray[i] = ^{ printf("hello, %d\n", i); };
        // WRONG: The block literal scope is the "for" loop.
    }
}
 
void dontDoThisEither() {
    void (^block)(void);
 
    int i = random():
    if (i > 1000) {
        block = ^{ printf("got i at: %d\n", i); };
        // WRONG: The block literal scope is the "then" clause.
    }
    // ...
}
复制代码

Debugging

能够设置断点并单步执行, 并使用invoke-block从GDB会话中调用一个block

$ invoke-block myBlock 10 20
复制代码

若是要传递C字符串则必须引用它

$ invoke-block doSomethingWithString "\"this string\""
复制代码

Effictive Objective-C 2.0中Block介绍

Block内部结构

每一个Objective C对象都占据着某个内存空间, 由于实例变量的个数及对象包含的关联数据互不相同, 因此每一个对象所占的内存区域也有大有小. block自己也是对象, 在存放快对象的内存区域中, 首个变量是指向Class对象的指针, 该指针叫作isa, 其他内存里含有block对象正常运转所需的各类信息, 上图描述了block对象的内存布局

  • invoke变量, 这是个函数指针, 指向block的实现代码, 函数原型至少须要接收一个void *型的参数, 这个参数表明block自己
  • descriptor变量是指向结构体的指针, 每一个block里都包含此结构体, 其中声明了block对象的整体大小, 还声明了copydispose这两个辅助函数所对应的函数指针, 辅助函数在拷贝及丢弃block对象时运行, 其中会执行一些操做, 好比: copy要保留捕获的对象, 而dispose则将之释放

block还会把它所捕获的全部变量都拷贝一份, 这些拷贝会放在descriptor变量后面, 捕获了多少变量, 就要占多少内存空间, 请注意, 拷贝的并非对象自己, 而是指向这些对象的指针变量, 在执行block对象的时候, 就会从内存中把这些捕获到的变量所有读取出来

栈block

  • 定义block的时候, 其所占的内存区域是分配在栈中的, block只在定义定义它的范围内有效
  • 编译器会给每一个block分配好栈内存, 而后等离开了相应的范围后, 编译器有可能会把分配给block的内存复写掉

堆block

  • 给栈block执行copy操做, 就能够把block从栈上复制到堆上, 拷贝的block能够在定义它的那个范围以外使用
  • 一旦block复制到堆上, block就成了带引用计数的对象了, 后续的copy操做都不会真的执行复制, 只是递增block对象的引用计数
  • 若是再也不使用堆block, 应将其释放, 而手动管理引用计数则须要自行调用release方法, 引用计数为0时, 内存空间被系统回收

全局block

  • 不会捕获任何状态(捕获外部变量), 运行时也无需有状态来参与的block
  • block所使用的的整个内存区域, 在编译器已经彻底肯定, 所以, 全局block能够声明在全局内存里, 不须要在每次用到的时候在栈中建立
  • 全局block的copy操做是个空操做, 由于全局block不会被系统回收, 至关于单例

全局block示例

void (^GlobalBlock)(void) {
    NSLog(@"This is a global block");
}
复制代码

Objective-C高级编程 iOS与OS X多线程和内存管理中Block介绍

其余程序语言中Block的名称

程序语言 Block名称
C + Blocks Block
Smalltalk Block
Ruby Block
LISP Lambda
Python Lambda
C++11 Lambda
Javascript Anonymous function

截获自动变量(书中写的是自动变量--就是局部变量)

截获自动变量值的实例以下

int main() {
    int dmy = 256;
    int val = 10;
    const char *fmt = "val = %d\n";
    void (^blk)(void) = ^{printf(fmt, val);};

    val = 2;
    fmt = "These values were changed. val = %d\n";

    blk();
    // 打印 val = 10

    return 0;
}
复制代码

在上面的源码中, Block语法的表达式使用的是它以前声明的自动变量fmt和val

  • Block表达式截获所使用的的自动变量的值, 及保存该自动变量的瞬间值
  • 在执行Block语法表达式以后(Block定义以后), 改写了Block中使用的自动变量的值, 也不会影响Block执行时自动变量的值, 执行结果是执行Block语法是自动变量的瞬间值

__block修饰符

实际上, 自动变量值截获的只能保存执行Block语法瞬间的值, 保存后就不能改写该值, 若是要在block内部修改捕获的变量的值, 须要经过添加__block修饰符

Block的实现

查看block的C++源码实现, 可使用clang编译器指令

clang -rewrite-objc 源代码文件
复制代码
int global_val = 1;
static int static_global_val = 2;

int main() {
   int val = 10;
   static int static_val = 3;
   __block int block_val = 4;
   __block NSObject *instance_val = [[NSObject alloc] init];
   void (^blk)(void) = ^{
       global_val *= 1; // 全局变量
       static_global_val *= 2; // 全局静态变量
       static_val *= 3; // 局部静态变量
       block_val = 1; // __block修饰变量
       instance_val = [[NSObject alloc] init]; // 对象
       printf("val: %d, block_val: %d\n", val, block_val);
   };

   val = 2;
   block_val = 3;

   blk();
   return 0;
}
复制代码

C++源码

int main() {
   int val = 10;
   static int static_val = 3;
   __attribute__((__blocks__(byref))) __Block_byref_block_val_0 
   block_val = {(void*)0,
                (__Block_byref_block_val_0 *)&block_val, 
                0, 
                sizeof(__Block_byref_block_val_0), 
                4};
                
   __attribute__((__blocks__(byref))) __Block_byref_instance_val_1 
   instance_val = {(void*)0,
                    (__Block_byref_instance_val_1 *)&instance_val, 
                    33554432, 
                    sizeof(__Block_byref_instance_val_1),
                    __Block_byref_id_object_copy_131,
                    __Block_byref_id_object_dispose_131, 
                    ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};
                    
                    
   void (*blk)(void) = ((void (*)())&__main_block_impl_0((void*)__main_block_func_0, 
                                                          &__main_block_desc_0_DATA, 
                                                          &static_val, 
                                                          val, 
                                                          (__Block_byref_block_val_0*)&block_val, 
                                                          (__Block_byref_instance_val_1 *)&instance_val, 
                                                          570425344));

   val = 2;
   (block_val.__forwarding->block_val) = 3;

   ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

    return 0;
}
复制代码

__main_block_impl_0结构

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;

  // 局部静态变量 指针形式引用
  int *static_val;

  // 通常局部变量
  int val;

  // __block的变量
  __Block_byref_block_val_0 *block_val; // by ref
  // __block的对象
  __Block_byref_instance_val_1 *instance_val; // by ref

  // 初始化构造函数
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int _val, __Block_byref_block_val_0 *_block_val, __Block_byref_instance_val_1 *_instance_val, int flags=0) : static_val(_static_val), val(_val), block_val(_block_val->__forwarding), instance_val(_instance_val->__forwarding) {
    impl.isa = &_NSConcreteStackBlock; // isa指向所属的类型, 此处代表block是对象
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
复制代码

__main_block_func_0函数

// block执行时候调用的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_block_val_0 *block_val = __cself->block_val; // bound by ref
  __Block_byref_instance_val_1 *instance_val = __cself->instance_val; // bound by ref
  int *static_val = __cself->static_val; // bound by copy
  int val = __cself->val; // bound by copy
       
       // 全局变量
       global_val *= 1;

       // 全局静态变量
       static_global_val *= 2;

       // 局部静态变量
       (*static_val) *= 3;

       // __block变量
       (block_val->__forwarding->block_val) = 1;

       // __block对象
       (instance_val->__forwarding->instance_val) = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
       printf("val: %d, block_val: %d\n", val, (block_val->__forwarding->block_val));
   }
复制代码

__main_block_desc_0_DATA结构体

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, 
                               sizeof(struct __main_block_impl_0), 
                               __main_block_copy_0, 
                               __main_block_dispose_0
                               };
复制代码

注意 添加了__block的变量都变成告终构体

__Block_byref_block_val_0结构

// block_val生成的结构体
struct __Block_byref_block_val_0 {
  void *__isa; // 说明也变成了对象
__Block_byref_block_val_0 *__forwarding;
 int __flags;
 int __size;
 int block_val;
};
复制代码

__Block_byref_instance_val_1结构

// __block修饰的instance_val
struct __Block_byref_instance_val_1 {
  void *__isa;
__Block_byref_instance_val_1 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSObject *instance_val;
};
复制代码

__block变量__Block_byref_block_val_0,__Block_byref_instance_val_1结构体并不在Block的__main_block_impl_0结构体中, 这样作是为了能够在多个Block中使用__block变量

截获变量类型

  • 局部变量

    • 基本数据类型
      • 直接截获其值, 不可修改
    • 对象类型
      • 连同对象全部权修饰符一块儿截获
  • 静态局部变量

    • 以指针形式截获, block内部能够直接改写
  • 全局变量

    • 不截获, block内部能够直接改写
  • 静态全局变量

    • 不截获, block内部能够直接改写
  • 静态局部变量,全局变量,静态全局变量不须要使用__block修饰符

  • __block修饰的基本数据类型的变量都会变成对象(见__Block_byref_block_val_0,__Block_byref_instance_val_1)

何时栈上的Block会复制到堆上

  • 调用Block的copy方法时
  • Block做为函数返回值时
  • 将Block复制给附有__strong修饰符id类型的类或Block类型的成员变量
  • 在方法命中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API传递Block时

在多个Block中使用__block变量时, 由于最早会将全部的Block配置在栈上,因此__block变量也会配置在栈上, 在任何一个Block从栈复制到堆时, __block变量也会一并从栈复制到堆并被该Block所持有, 当剩下的Block从栈复制到堆时, 被复制的Block持有__block变量, 并增长__block变量的引用计数

__forwarding指针

  • 栈上的block的__block变量结构体的__forwarding指针指向本身
  • 栈Block进行copy操做后, 生成堆Block, 栈Block的__block变量结构体的__forwarding指针指向堆Block__block变量的结构体,堆上的block的__block变量结构体的__forwarding指针指向本身
    • 经过该功能, 不管是在Block语法中, Block语法外使用__block变量, 仍是__block变量配置在栈上或堆上, 均可以顺利地访问到同一个__block变量

理解若有错误 望指正 转载请说明出处

相关文章
相关标签/搜索