ios block

from http://www.cnbluebox.com/?p=255
php

引言 html

使用block经有一段时间了,感受本身了解的还行,可是几天前看到CocoaChina上一个关于block的小测试主题 : 【小测试】你真的知道blocksObjective-C中是怎么工做的吗?,发现居然作错了几道, 才知道本身想固然的理解是错误的,因此抽时间学习了下,而且经过一些测试代码进行测试,产生这篇博客。 闭包

Block简介(copy一段) app

BlockC语言的扩展,并非高新技术,和其余语言的闭包或lambda达式是一回事。须要注意的是因为Objective-CiOS中不支持GC机制,使用Block须本身管理内存,而内存管理正是使用Block坑最多的地方,错误的内存管理 要么致使return cycle内存泄漏要么内存被提早释放致使crash Block的使用很像函数指针,不过与函数最大的不一样是:Block能够访问函数之外、词法做用域之内的外部变量的值。换句话说,Block仅 实现函数的功能,还能携带函数的执行环境。 dom


能够这样理解,Block实包含两个部份内容 async

  1. Block执行的代码,这是在编译的时候已经生成好的;
  2. 一个包含Block执行时须要的全部外部变量值的数据结构。 Block将使用到的、做用域附近到的变量的值创建一份快照拷贝到栈上。

Block与函数另外一个不一样是,Block相似ObjC对象,可使用自动释放池管理内存(但Block并不彻底等同于ObjC对象,后面将详细说明)。 函数

Block基本语法 学习

基本语法在本文就不赘述了,同窗们自学。 测试

Block的类型与内存管理 spa

根据Block在内存中的位置分为三种类型NSGlobalBlockNSStackBlock, NSMallocBlock

  • NSGlobalBlock:相似函数,位于text段;
  • NSStackBlock:位于栈内存,函数返回后Block将无效;
  • NSMallocBlock:位于堆内存。

1NSGlobalBlock以下,我们能够经过是否引用外部变量识别,未引用外部变量即为NSGlobalBlock,能够当作函数使用。

1

2

3

4

5

6

7

8

9

{

    //create a NSGlobalBlock

    float (^sum)(float, float) = ^(float a, float b){

 

        return a + b;

    };

 

    NSLog(@"block is %@", sum); //block is <__NSGlobalBlock__: 0x47d0>

}

2NSStackBlock以下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

{

    NSArray *testArr = @[@"1", @"2"];

 

    void (^TestBlock)(void) = ^{

 

        NSLog(@"testArr :%@", testArr);

    };

 

    NSLog(@"block is %@", ^{

 

        NSLog(@"test Arr :%@", testArr);

    });

    //block is <__NSStackBlock__: 0xbfffdac0>

    //打印可看出block是一个 NSStackBlock, 即在栈上, 当函数返回block将无效

 

    NSLog(@"block is %@", TestBlock);

    //block is <__NSMallocBlock__: 0x75425a0>

    //上面这句在非arc中打印是 NSStackBlock, 可是在arc中就是NSMallocBlock

    //即在arc中默认会将block栈复制到堆上,而在非arc中,则须要手动copy.

}

3NSMallocBlock只须要NSStackBlock进行copy操做就能够获取,可是retain操做就不行,会在下面说明

Blockcopyretainrelease操做 仍是copy一段)

不一样于NSObjeccopyretainrelease操做:

  • Block_copycopy等效,Block_releaserelease等效;
  • Block无论是retaincopyrelease都不会改变引用计数retainCountretainCount终是1
  • NSGlobalBlockretaincopyrelease操做都无效;
  • NSStackBlockretainrelease操做无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即便retain也没用。容易犯的错误是[[mutableAarry addObject:stackBlock],(补:在arc中不用担忧此问题,由于arc中会默认将实例化的block贝到堆上)在函数出栈后,从mutableAarry中取到的stackBlock经被回收,变成了野指针。正确的作法是先将stackBlock copy到堆上,而后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copycopy以后生成新的NSMallocBlock类型对象。
  • NSMallocBlock支持retainrelease,虽然retainCount终是1,但内存管理器中仍然会增长、减小计数。copy以后不会生成新的对象,只是增长了一次引用,相似retain
  • 尽可能不要Block使用retain操做。

Block对外部变量的存取管理

基本数据类型

1、局部变量

局部自动变量,在Block中只读。Block义时copy变量的值,在Block中做为常量使用,因此即便变量的值在Block外改变,也不影响他在Block中的值。

1

2

3

4

5

6

7

8

9

10

11

{

    int base = 100;

    long (^sum)(int, int) = ^ long (int a, int b) {

 

        return base + a + b;

    };

 

    base = 0;

    printf("%ld\n",sum(1,2));

    // 这里输出是103,而不是3, 为块内base为拷贝的常量 100

}

2STATIC饰符的全局变量

为全局变量或静态变量在内存中的地址是固定的,Block读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量.

1

2

3

4

5

6

7

8

9

10

11

12

13

{

    static int base = 100;

    long (^sum)(int, int) = ^ long (int a, int b) {

        base++;

        return base + a + b;

    };

 

    base = 0;

    printf("%ld\n",sum(1,2));

    // 这里输出是4,而不是103, base设置为了0

    printf("%d\n", base);

    // 这里输出1 sum中将base++

}

3__BLOCK饰的变量

Block变量,被__block饰的变量称做Block变量。 基本类型的Block变量等效于全局变量、或静态变量。

注:BLOCK被另外一个BLOCK使用时,另外一个BLOCKCOPY到堆上时,被使用的BLOCK也会被COPY。但做为参数的BLOCK是不会发生COPY

OBJC对象

block对于objc对象的内存管理较为复杂,这里要分static global local block变量分析、还要分非arcarc分析

ARC中的变量

先看一段代(arc)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

@interface MyClass : NSObject {

    NSObject* _instanceObj;

}

@end

 

@implementation MyClass

 

NSObject* __globalObj = nil;

 

- (id) init {

    if (self = [super init]) {

        _instanceObj = [[NSObject alloc] init];

    }

    return self;

}

 

- (void) test {

    static NSObject* __staticObj = nil;

    __globalObj = [[NSObject alloc] init];

    __staticObj = [[NSObject alloc] init];

 

    NSObject* localObj = [[NSObject alloc] init];

    __block NSObject* blockObj = [[NSObject alloc] init];

 

    typedef void (^MyBlock)(void) ;

    MyBlock aBlock = ^{

        NSLog(@"%@", __globalObj);

        NSLog(@"%@", __staticObj);

        NSLog(@"%@", _instanceObj);

        NSLog(@"%@", localObj);

        NSLog(@"%@", blockObj);

    };

    aBlock = [[aBlock copy] autorelease];

    aBlock();

 

    NSLog(@"%d", [__globalObj retainCount]);

    NSLog(@"%d", [__staticObj retainCount]);

    NSLog(@"%d", [_instanceObj retainCount]);

    NSLog(@"%d", [localObj retainCount]);

    NSLog(@"%d", [blockObj retainCount]);

}

@end

 

int main(int argc, char *argv[]) {

    @autoreleasepool {

        MyClass* obj = [[[MyClass alloc] init] autorelease];

        [obj test];

        return 0;

    }

}

执行结果为1 1 1 2 1

__globalObj__staticObj在内存中的位置是肯定的,因此Block copy时不会retain对象。

_instanceObjBlock copy时也没有直接retain _instanceObj对象自己,但会retain self。因此在Block中能够直接读写_instanceObj变量。

localObjBlock copy时,系统自动retain对象,增长其引用计数。

blockObjBlock copy时也不会retain

ARC中的变量测试

因为arc中没有retainretainCount的概念。只有强引用和弱引用的概念。当一个变量没有__strong的指针指向它时,就会被系统释放。所以咱们能够经过下面的代码来测试。

码片断1(globalObject全局变量)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

NSString *__globalString = nil;

 

- (void)testGlobalObj

{

    __globalString = @"1";

    void (^TestBlock)(void) = ^{

 

        NSLog(@"string is :%@", __globalString); //string is http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif" alt=":(" class="wp-smiley"> null)

    };

 

    __globalString = nil;

 

    TestBlock();

}

 

- (void)testStaticObj

{

    static NSString *__staticString = nil;

    __staticString = @"1";

 

    printf("static address: %p\n", &__staticString);   //static address: 0x6a8c

 

    void (^TestBlock)(void) = ^{

 

        printf("static address: %p\n", &__staticString); //static address: 0x6a8c

 

        NSLog(@"string is : %@", __staticString); //string is http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif" alt=":(" class="wp-smiley"> null)

    };

 

    __staticString = nil;

 

    TestBlock();

}

 

- (void)testLocalObj

{

    NSString *__localString = nil;

    __localString = @"1";

 

    printf("local address: %p\n", &__localString); //local address: 0xbfffd9c0

 

    void (^TestBlock)(void) = ^{

 

        printf("local address: %p\n", &__localString); //local address: 0x71723e4

 

        NSLog(@"string is : %@", __localString); //string is : 1

    };

 

    __localString = nil;

 

    TestBlock();

}

 

- (void)testBlockObj

{

    __block NSString *_blockString = @"1";

 

    void (^TestBlock)(void) = ^{

 

        NSLog(@"string is : %@", _blockString); // string is http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif" alt=":(" class="wp-smiley"> null)

    };

 

    _blockString = nil;

 

    TestBlock();

}

 

- (void)testWeakObj

{

    NSString *__localString = @"1";

 

    __weak NSString *weakString = __localString;

 

    printf("weak address: %p\n", &weakString);  //weak address: 0xbfffd9c4

    printf("weak str address: %p\n", weakString); //weak str address: 0x684c

 

    void (^TestBlock)(void) = ^{

 

        printf("weak address: %p\n", &weakString); //weak address: 0x7144324

        printf("weak str address: %p\n", weakString); //weak str address: 0x684c

 

        NSLog(@"string is : %@", weakString); //string is :1

    };

 

    __localString = nil;

 

    TestBlock();

}

由以上几个测试咱们能够得出:
1
、只有在使用local变量时,block会复制指针,且强引用指针指向的对象一次。其它如全局变量、static变量、block变量等,block不会拷贝指针,只会强引用指针指向的对象一次。
2
、即时标记了为__weak__unsafe_unretainedlocal变量。block仍会强引用指针对象一次。(这个不太明白,由于这种写法可在后面避免循环引用的问题)

环引用retain cycle

环引用指两个对象相互强引用了对方,即retain对方,从而致使谁也释放不了谁的内存泄露问题。如声明一个delegate时通常用assign而不能用retainstrong,因为你一旦那么作了,很大可能引发循环引用。在以往的项目中,我几回用动态内存检查发现了循环引用致使的内存泄露。

这里讲的是block的循环引用问题,由于block在拷贝到堆上的时候,会retain其引用的外部变量,那么若是block中若是引用了他的宿主对象,那颇有可能引发循环引用,如:

1

2

3

4

self.myblock = ^{

 

            [self doSomething];

        };

为测试循环引用,写了些测试代码用于避免循环引用的方法,以下,(只有arc的,懒得作非arc测试了)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

- (void)dealloc

{

 

    NSLog(@"no cycle retain");

}

 

- (id)init

{

    self = [super init];

    if (self) {

 

#if TestCycleRetainCase1

 

        //会循环引用

        self.myblock = ^{

 

            [self doSomething];

        };

#elif TestCycleRetainCase2

 

        //会循环引用

        __block TestCycleRetain *weakSelf = self;

        self.myblock = ^{

 

            [weakSelf doSomething];

        };

 

#elif TestCycleRetainCase3

 

        //不会循环引用

        __weak TestCycleRetain *weakSelf = self;

        self.myblock = ^{

 

            [weakSelf doSomething];

        };

 

#elif TestCycleRetainCase4

 

        //不会循环引用

        __unsafe_unretained TestCycleRetain *weakSelf = self;

        self.myblock = ^{

 

            [weakSelf doSomething];

        };

 

#endif

 

        NSLog(@"myblock is %@", self.myblock);

 

    }

    return self;

}

 

- (void)doSomething

{

    NSLog(@"do Something");

}

 

int main(int argc, char *argv[]) {

    @autoreleasepool {

        TestCycleRetain* obj = [[TestCycleRetain alloc] init];

        obj = nil;

        return 0;

    }

}

通过上面的测试发现,在加了__weak__unsafe_unretained变量引入后,TestCycleRetain方法能够正常执行dealloc方法,而不转换和用__block转换的变量都会引发循环引用。
所以防止循环引用的方法以下:
__unsafe_unretained TestCycleRetain *weakSelf = self;

end


补充:

In manual reference counting mode, __block id x; has the effect of not retaining x. In ARC mode, __block id x; defaults to retaining x (just like all other values). To get the manual reference counting mode behavior under ARC, you could use __unsafe_unretained __block id x;. As the name __unsafe_unretained implies, however, having a non-retained variable is dangerous (because it can dangle) and is therefore discouraged. Two better options are to either use __weak (if you don’t need to support iOS 4 or OS X v10.6), or set the __block value to nilto break the retain cycle.



本文来自台湾的某开发人员的博客,被墙,感受讲的比较易懂,因此引过来。文字简体化了,原来是繁体,变数=变量,这个注意一下。

本文的顺序是层层深刻的,要想简单的了解,只看X.1 初探Block就行了

本章学习目标:

1. 了解何谓block。

2. 了解block的使用方法。

Block 是iOS在4.0以后新增的程式语法,严格来讲block的概念并不算是基础程式设计的范围,对初学者来讲也不是很容易了解,可是在iOS SDK 4.0以后,block几乎出如今全部新版的API之中,换句话说,若是不了解block这个概念就没法使用SDK 4.0版本之后的新功能,所以虽然block自己的语法有点难度,但为了使用iOS的新功能咱们仍是得硬着头皮去了解这个新的程式概念。

在这一章的目标以了解如何使用block为主而不深刻探讨block底层的运做方式,至于有些初学者较少遇到的辞汇如「词法做用域(lexical scope)」等,本章将再也不多作解释,待有兴趣的读者去请教Google大神吧。


X.1 初探Block

在这一小节咱们先用一些简单范例来导入block的概念。

X.1.1 宣告和使用Block

咱们使用「^」运算子来宣告一个block变数,并且在block的定义最后面要加上「;」来表示一个完整的述句(也就是将整个block定义视为前面章节所介绍的简单述句,由于整个定义必须是一个完整的句子,因此必须在最后面加上分号),下面是一个block的范例:

 1: int multiplier = 7 ;
 2: int (^myBlock)( int ) = ^( int num)
 3: {
 4: return num * multiplier;
 5: };

咱们使用下图来解释这个范例(请将文字框的字翻译以下):

咱们宣告一个「myBlock」变数,用「^」符号来表示这是一个block。

这是block的完整定义,这个定义将会指定给「myBlock」变数。

表示「myBlock」是一个回传值为整数(int)的block。

它有一个参数,型态也是整数。

这个参数的名字叫作「num」。

这是block的内容。

值得注意的地方是block可使用和自己定义范围相同的变数,能够想像在上面的例子中 multiplier 和 myBlock 都是某一个函数内定义的两个变数也就是这个变数都在某个函数两个大括号「{」和「 }」中间的区块,由于它们的有效范围是相同的,所以在block中就能够直接使用 multiplier 这个变数,此外当把block定义成一个变数的时,咱们能够直接像使用通常函数般的方式使用它:

 1: int multiplier = 7 ;
 2: int (^myBlock)( int ) = ^( int num)
 3: {
 4: return num * multiplier;
 5: };
 6: printf ( "%d" , myBlock( 3 ));
 7: //结果会打印出21

X.1.2 直接使用Block

在不少状况下,咱们并不须要将block宣告成变数,反之咱们能够直接在须要使用block的地方直接用内嵌的方式将block的内容写出来,在下面的例子中qsort_b函数,这是一个相似传统的qsort_t函数,可是直接使用block作为它的参数:

 1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" };
 2: qsort_b (myCharacters, 3 ,
 3: sizeof ( char *),
 4: ^( const void *l, const void *r)//block部分
 5: {
 6: char *left = *( char **)l;
 7: char *right = *( char **)r;
 8: return strncmp (left, right, 1 );
 9: } //end
 10: );

X.1.3 __block 变量

通常来讲,在block内只能读取在同一个做用域的变数并且没有办法修改在block外定义的任何变数,此时若咱们想要这些变数可以在block中 被修改,就必须在前面挂上__block的修饰词,以上面第一个例子中的 multiplier 来讲,这个变数在 block 中是惟读的,因此 multiplier = 7 指定完后,在 block 中的 multiplier 就只能是 7 不能修改,若咱们在 block 中修改 multiplier ,在编辑时就会产生错误,所以若想要在 block 中修改 multiplier ,就必须在 multiplier 前面加上 __block 的修饰词,请参考下面的范例:

 1: __block int multiplier = 7 ;
 2: int (^myBlock)( int ) = ^( int num)
 3: {
 4: if (num > 5 )
 5: {
 6: multiplier = 7 ;
 7: }
 8: else
 9: {
 10: multiplier = 10 ;
 11: }
 12: return num * multiplier;
 13: };

 


X.2 Block 概要

 

Block 提供咱们一种可以将函数程式码内嵌在通常述句中的方法,在其余语言中也有相似的概念称作「closure」,可是为了配合Objective-C的贯例,咱们一概将这种用法称为「block」

X.2.1 Block 的功能

Block 是一种具备匿名功能的内嵌函数,它的特性以下:

如通常的函数般能拥有带有型态的参数。

拥有回传值。

能够撷取被定义的词法做用域(lexical scope)状态。

能够选择性地修改词法做用域的状态。

注:词法做用域(lexical scope)能够想像成是某个函数两个大括号中间的区块,这个区块在程式执行时,系统会将这个区块放入堆叠记忆体中,在这个区块中的宣告的变数就像是咱们 常听到的区域变数,当咱们说block能够撷取同一词法做用域的状态时能够想像block变数和其余区域变数是同一个层级的区域变数(位于同一层的堆叠 里),而block的内容能够读取到和他同一层级的其余区域变数。

咱们能够拷贝一个block,也能够将它丢到其余的执行绪中使用,基本上虽然block在iOS程式开发中可使用在C/C++开发的程式片断,也能够在Objective-C中使用,不过在系统的定义上,block永远会被视为是一个Objective-C的物件。

X.2.2 Block 的使用时机

Block 通常是用来表示、简化一小段的程式码,它特别适合用来创建一些同步执行的程式片断、封装一些小型的工做或是用来作为某一个工做完成时的回传呼叫(callback) 。

在新的iOS API中block被大量用来取代传统的delegate和callback,而新的API会大量使用block主要是基于如下两个缘由:

能够直接在程式码中撰写等会要接着执行的程式,直接将程式码变成函数的参数传入函数中,这是新API最常使用block的地方。

能够存取区域变数,在传统的callback实做时,若想要存取区域变数得将变数封装成结构才能使用,而block则是能够很方便地直接存取区域变数。

 


X.3 宣告和创建Block

 

X.3.1 宣告Block的参考(Reference)

Block 变数储存的是一个block的参考,咱们使用相似宣告指标的方式来宣告,不一样的是这时block变数指到的地方是一个函数,而指标使用的是「*」,block则是使用「^」来宣告,下面是一些合法的block宣告:

 1: /* 回传void ,参数也是void 的block*/
 2: void (^blockReturningVoidWithVoidArgument)( void );
 3: /* 回传整数,两个参数分别是整数和字元型态的block*/
 4: int (^blockReturningIntWithIntAndCharArguments)( int , char );
 5: /* 回传void ,含有10 个block 的阵列,每一个block 都有一个型态为整数的参数*/
 6: void (^arrayOfTenBlocksReturningVoidWinIntArgument[ 10 ])( int );
 7: X.3.2 创建一个Block
 8: 
 9: 咱们使用「^」来开始一个block,并在最后使用「;」来表示结束,下面的范例示范了一个block变数,而后再定义一个block把它指定给block变数:
 10: 
 11: int (^oneFrom)( int ); /* 宣告block 变数*/
 12: /* 定义block 的内容并指定给上面宣告的变数*/
 13: oneFrom = ^(int anInt)
 14: {
 15: return anInt = - 1 ;
 16: };

X.3.3 全域的Block

我在能够在档案中宣告一个全域的block,请参考如下范例:

 1: int GlobalInt = 0 ;
 2: int (^getGlobalInt)( void ) = ^ ( void ) { return GlobalInt ;};

 


X.4 Block 和变量

 

接下来的这一小节咱们将会介绍block和变数之间的互动。

X.4.1 变数的型态

咱们能够在block中遇到日常在函数中会遇到的变数类型:

l 全域(global)变数或是静态的区域变数(static local)。

l 全域的函数。

l 区域变数和由封闭领域(enclosing scope)传入的参数。

除了上述以外block额外支援了另外两种变数:

在函数内可使用__block 变数,这些变数在block中是可被修改的。

汇入常数(const imports)。

此外,在方法的实做里,block可使用Objective-C的实体变数(instance variable)。

下列的规则能够套用到在block中变数的使用:

能够存取全域变数和在同一领域(enclosing lexical scope)中的静态变数。

能够存取传入block的参数(使用方式和传入函数的参数相同)。

在同一领域的区域变数在block中将视为常数(const)。

能够存取在同一领域中以__block 为修饰词的变数。

在block中宣告的区域变数,使用方式和日常函数使用区域变数的方式相同。

下面的例子介绍了区域变数(上述第三点)的使用方式:

 1: int x = 123 ;
 2: void (^printXAndY)( int ) = ^( int y)
 3: {
 4: printf ( "%d %d\n" , x, y);
 5: };
 6: // 将会印出123 456
 7: printXAndY( 456 );
 8: 就如上面第三点所提到的,在上例中的int x = 123的变量x,在传入block后将视同常数,所以若咱们在block中试着去修改x的值时就会产生错误,下面的例子将会没法经过编译:
 9: 
 10: int x = 123 ;
 11: void (^printXAndY)( int ) = ^( int y)
 12: {
 13: // 下面这一行是错的,由于x 在这是一个常数不能被修改。
 14: x = x + y;
 15: printf ( "%d %d\n" , x, y);
 16: };

若在block中想要修改上面的变数x,必须将x宣告加上修饰词__block,请参考接下来这一小节的介绍。

X.4.2 __block 型态变数

咱们能够藉由将一个由外部汇入block的变数放上修饰词__block来让这个变数由惟读变成能够读和写,不过有一个限制就是传入的变数在记忆体 中必须是一个占有固定长度记忆体的变数,__block修饰词没法使用于像是变更长度的阵列这类不定长度的变数,请参考下面的范例:

 1: // 加上__block 修饰词,因此能够在block 中被修改。
 2: __block int x = 123 ;
 3: void (^printXAndY)( int ) = ^( int y)
 4: {
 5: x = x + y;
 6: printf ( "%d %d\n" , x, y);
 7: };
 8: // 将会印出579 456
 9: printXAndY( 456 );
 10: //x 将会变成 579;
 11: 下面咱们使用一个范例来介绍各种型的变数和block之间的互动:
 12: 
 13: extern NSInteger CounterGlobal;
 14: static NSInteger CounterStatic;
 15: {
 16: NSInteger localCounter = 42 ;
 17: __block char localCharacter;
 18: void (^aBlock)( void ) = ^( void )
 19: {
 20: ++ CounterGlobal ; //能够存取。
 21: ++ CounterStatic ; //能够存取。 
 22: CounterGlobal = localCounter; //localCounter在block 创建时就不可变了。
 23: localCharacter = 'a' ; //设定外面定义的localCharacter 变数。
 24: };
 25: ++localCounter; //不会影响的block 中的值。
 26: localCharacter = 'b' ;
 27: aBlock(); //执行block 的内容。
 28: //执行完后,localCharachter 会变成'a'
 29: }

X.4.3 物件和Block变数

Block 支援在Objective-C、C++物件和其余block中看成变数来使用,不过由于在大部分的状况咱们都是使用Objective-C的撰写程式,因 此在这一小节咱们仅针对Objective-C的状况进行介绍,至于其余两种状况就留给有兴趣的读者再自行深刻研究了。

x.4.3.1 Objective-C 物件

在拥有参考计数(reference-counted)的环境中,若咱们在block中参考到Objective-C的物件,在通常的状况下它将会自动增长物件的参考计数,不过若以__block为修饰词的物件,参考计数则是不受影响。

若是咱们在Objective-C的方法中使用block时,如下几个和记忆体管理的事是须要额外注意的:

l 若直接存取实体变数(instance variable),self的参考计数将被加1。

l 若透过变数存取实体变数的值,则只变数的参考计数将被加1。

如下程式码说明上面两种状况,在这个假设instanceVariable是实体变数:

 1: dispatch_async (queue, ^{
 2: // 由于直接存取实体变数instanceVariable ,因此self 的retain count 会加1
 3: doSomethingWithObject (instanceVariable);
 4: });
 5: id localVaribale = instanceVariable;
 6: dispatch_async (queue, ^{
 7: //localVariable 是存取值,因此这时只有localVariable 的retain count 加1
 8: //self 的 return count  并不会增长。
 9: doSomethingWithObject (localVaribale);
 10: });

 


X.5 使用Block

 

这一小节咱们将会对block的使用方式作一些初步的介绍

X.5.1 呼叫一个Block

当block宣告成一个变数时,咱们能够像使用通常函数的方式来使用它,请参考下面两个范例:

 1: int (^oneFrom)( int ) = ^( int anInt) {
 2: return anInt - 1 ;
 3: };
 4: printf ( "1 from 10 is %d" , oneFrom( 10 ));
 5: //结果会显示:1 from 10 is 9
 6: float (^distanceTraveled)( float , float , float ) = ^( float startingSpeed, float acceleration, float time)
 7: {
 8: float distance = (startingSpeed * time) + ( 0.5 * acceleration * time * time);
 9: return distance;
 10: };
 11: float howFar = distanceTraveled( 0.0 , 9.8 , 1.0 );
 12: //howFar会变成4.9

在通常常见的状况中,如果将block当作是参数传入函数,咱们一般会使用「内嵌」的方式来使用block。

X.5.2 将Block看成函数的参数

咱们能够像使用通常函数使用参数的方式,将block以函数参数的型式传入函数中,在这种状况下,大多数咱们使用block的方式将不会倾向宣告block而是直接之内嵌的方式来将block传入,这也是目前新版SDK中主流的作法,咱们将补充前面章节的例子来讲明:

 1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" };
 2: qsort_b (myCharacters, 3 , sizeof ( char *),
 3: ^( const void *l, const void *r)
 4: {
 5: char *left = *( char **)l;
 6: char *right = *( char **)r;
 7: return strncmp (left, right, 1 );
 8: } // 这里是block 的终点。
 9: );
 10: // 最后的结果为:{"Charles Condomine", "George", "TomJohn"}

在上面的例子中,block自己就是函数参数的一部分,在下一个例子中dispatch_apply函数中使用block,dispatch_apply的定义以下:

 1: void
 2: dispatch_apply( size_t iterations, dispatch_queue_t queue, void (^block)( size_t ));
 3: 这个函数将一个block提交到发送伫列(dispatch queue)中来执行多重的呼叫,只有当伫列中的工做都执行完成后才会回传,这个函数拥有三个变数,而最后一个参数就是block ,请参考下面的范例:
 4: 
 5: size_t count = 10 ;
 6: dispatch_queue_t queue =
 7: dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
 8: dispatch_apply (count, queue, ^( size_t i) {
 9: printf ( "%u\n" , i);
 10: });

X.5.3 将Block看成方法的参数

在SDK中提供了许多使用block的方法,咱们能够像传递通常参数的方式来传递block,下面这个范例示范如何在一个阵列的前5笔资料中取出咱们想要的资料的索引值:

 1: // 全部的资料
 2: NSArray *array = [ NSArray arrayWithObjects : @"A" , @"B" , @"C" , @"A" , @"B" , @"Z" , @"G" , @"are" , @" Q" ,nil ];
 3: // 咱们只要这个集合内的资料
 4: NSSet *filterSet = [ NSSet setWithObjects : @"A" , @"B" , @"Z" , @"Q" , nil ];
 5: BOOL (^test)( id obj, NSUInteger idx, BOOL *stop);
 6: test = ^ ( id obj, NSUInteger idx, BOOL *stop) {
 7: // 只对前5 笔资料作检查
 8: if (idx < 5 ) {
 9: if ([filterSet containsObject : obj]) {
 10: return YES ;
 11: }
 12: }
 13: return NO ;
 14: };
 15: NSIndexSet *indexes = [array indexesOfObjectsPassingTest :test];
 16: NSLog ( @"indexes: %@" , indexes);
 17: // 结果:indexes: <NSIndexSet: 0x6101ff0>[number of indexes: 4 (in 2 ranges), indexes: (0-1 3-4)]
 18: // 前5笔资料中,有4笔符合条件,它们的索引值分别是0-1, 3-4
X.5.4 该避免的使用方式

在下面的例子中,block是for回圈的区域变数所以在使用上必须避免将区域的block指定给外面宣告的block:

 1: // 这是错误的范例,请勿在程式中使用这些语法!!
 2: void dontDoThis() {
 3: void (^blockArray[3])(void); // 3 个block 的阵列
 4: for (int i = 0; i < 3; ++i) {
 5: blockArray[i] = ^{ printf("hello, %d\n", i); };
 6: // 注意: 这个block 定义仅在for 回圈有效。
 7: }
 8: }
 9: void dontDoThisEither() {
 10: void (^block)(void);
 11: int i = random():
 12: if (i > 1000) {
 13: block = ^{ printf("got i at: %d\n", i); };
 14: // 注意: 这个block 定义仅在if 后的两个大括号中有效。
 15: }
 16: // ...
 17: }
相关文章
相关标签/搜索