Block 它是iOS于4.0新的程序语法之后,于iOS SDK 4.0之后,block应用几乎无处不在。

在其他语言中也有类似的概念,称为闭包(closure),实例object C兄弟swift 中闭包(swift 闭包具体解释)的使用跟 OC的block一样重要。总的来说:

Block是C语言的

Block是一个数据类型

Block 是一个提前准备好的代码。在须要的时候运行

1. block作用:

Block用来封装一段代码,能够在不论什么时候运行;

  • Block能够作为函数參数或者函数的返回值,而其本身又能够带输入參数或返回值。
  • 苹果官方建议尽量多用block。在多线程、异步任务 、集合遍历、集合排序、动画转场用的非常多

在新的iOS API中block被大量用来代替传统的delegate和callback,而新的API会大量使用block主要是基于以下两个原因:

A. 能够直接在block代码块中写等会要接着运行的代码,直接把block变成函数的參数传入函数中,这是新API最常使用block的地方。

B. 能够存取局部变量,在传统的callback操作时,若想要存取局部变量得将变量封装成结构体才干使用,而block则是能够非常方便地直接存取局部变量。

2. Block的定义:

定义时。把block当成数据类型

特点:

1. 类型比函数定义多了一个 ^

2. 设置数值,有一个 ^,内容是 {} 括起的一段代码

(1)基本定义方式

  1. /*
  2. *1.最简单的定义方式:
  3. *格式:void (^myBlock)() = ^ { // 代码实现; }
  4. */
  5. void (^myBlock)() = ^ {
  6. NSLog(@"hello");
  7. };
  8. // 运行时。把block当成函数
  9. myBlock();
  10. /*
  11. *2.定义带參数的block:
  12. *格式:void (^block名称)(參数列表) = ^ (參数列表) { // 代码实现; }
  13. */
  14. void (^sumBlock)(int, int) = ^ (int x, int y) {
  15. NSLog(@"%d", x + y);
  16. };
  17. sumBlock(10, 20);
  18. /*
  19. *3.定义带返回值的block
  20. *格式:返回类型 (^block名称)(參数列表) = ^ 返回类型 (參数列表) { // 代码实现; }
  21. */
  22. int (^sumBlock2)(int, int) = ^ int (int a, int b) {
  23. return a + b;
  24. };
  25. NSLog(@"%d", sumBlock2(4, 8));

(2) block 指针

Block Pointer是这样定义的:

回传值 (^名字) (參数列);

  1. //声明一个名字为square的Block Pointer,其所指向的Block有一个int输入和int输出
  2. int (^square)(int);
  3. //block 指针square的内容
  4. square = ^(int a){ return a*a ; };
  5. //调用方法,感觉是是不是非常像function的使用方法?
  6. int result = square(5);
  7. NSLog(@"%d", result);

(3) 用typedef先声明类型,再定义变量进行赋值

  1. typedef int (^MySum)(int,int);
  2. MySum sum = ^(int a,int b)
  3. {
  4. return a + b;
  5. };

(4) block 訪问外部变量

可是block使用有个特点。Block能够訪问局部变量,可是不能改动:

  1. int sum = 10;
  2. int (^MyBlock)(int) = ^(int num)
  3. {
  4. sum++;//编译报错
  5. return num * sum;
  6. };

假设要改动就要加关键字 __block (以下具体说明):

__block int sum =10;

(5) block 与函数指针

以下比較下函数指针与block异同:

  • 定义函数指针 int (*myFn)();

    调用函数指针 (*myFn)(10, 20);

  • 定义Block int (^MyBlocks)(int,int);

    调用Blocks MyBlocks(10, 20);

3. block訪问外部变量

block 訪问外部变量有几个特点必须知道:

  • block内部能够訪问外部变量。
  • 默认情况下block内部不能改动外面的局部变量;
  • 给局部变量加上关键字_block,这个局部变量就能够在block内部改动;

block中能够訪问外部变量。可是不能改动它。否则编译错误。可是能够改变全局变量、静态变量(static)、全局静态变量。

上面的特点是有原因滴:

A. 为何不让改动变量:这个是编译器决定的。理论上当然能够改动变量了。仅仅只是block捕获的是外部变量的副本,名字一样。

为了不给开发人员迷惑,干脆不让赋值。

道理有点像:函数參数。要用指针,不然传递的是副本(大家想起那个经典的两个数调换值的问题了吧)。

B. 能够改动静态变量的值

静态变量属于类的,不是某一个变量。所以block内部不用调用cself指针。

所以block能够调用。

(1) __block存储类型

通过__block存储类型修饰符, 变量在block中可被改动。__block存储跟register、auto和static存储类型相似(可是之间相互排斥),用于局部变量。

__block变量存储在堆区,因此。这个block使用的外部变量,将会在栈结束被留下来。

从优化角度考虑,block存储在栈上,假设block被拷贝(通过Block_copy或者copy),变量被复制到堆。因此__block变量的地址就会改变。

__block变量还有两个限制,他们不能是可变数组(NSMutableArray),不能是结构体(structure)。

__block 变量的内部实现要复杂很多,__block 变量事实上是一个结构体对象,拷贝的是指向该结构体对象的指针

(2) block訪问外部变量

上面已经说过,默认block 訪问的外部变量是仅仅读属性的,若要对外部变量进行读写,须要在定义外部变量时加一个 __block, 示比例如以下:

  1. //演示样例1:block訪问外部变量
  2. void demoBlock1()
  3. {
  4. int x = 10;
  5. NSLog(@"定义前 %p", &x);// 局部变量在栈区
  6. // 在定义block的时候,假设引用了外部变量,默认是把外部变量当做是常量编码到block当中,而且把外部变量copy到堆中,外部变量值为定义block时变量的数值
  7. // 假设兴许再改动x的值,默认不会影响block内部的数值变化!
  8. // 在默认情况下。不同意block内部改动外部变量的数值!
  9. 由于会破坏代码的可读性,不易于维护!
  10. void(^myBlock)() = ^ {
  11. NSLog(@"%d", x);
  12. NSLog(@"in block %p", &x); // 堆中的地址
  13. };
  14. //输出是10,由于block copy了一份x到堆中
  15. NSLog(@"定义后 %p", &x); // 栈区
  16. x = 20;
  17. myBlock();
  18. }
  1. //演示样例2:在block中改动外部变量
  2. void demoBlock2()
  3. {
  4. // 使用 __block,说明不在关心x数值的具体变化
  5. __block int x = 10;
  6. NSLog(@"定义前 %p", &x); // 栈区
  7. // !定义block时,假设引用了外部使用__block的变量,在block定义之后, block外部的x和block内部的x指向了同一个值,内存地址同样
  8. void (^myBlock)() = ^ {
  9. x = 80;
  10. NSLog(@"in block %p", &x); // 堆区
  11. };
  12. NSLog(@"定义后 %p", &x); // 堆区
  13. myBlock();
  14. NSLog(@"%d", x);
  15. //打印x的值为8,且地址在堆区中
  16. }

以下的样例就有点难度了。让我们看下block对指针变量的訪问

  1. //样例3:block对指针变量的訪问
  2. void demoBlock3()
  3. {
  4. // !指针记录的是地址
  5. NSMutableString *strM = [NSMutableString stringWithString:@"zhangsan"];
  6. //strM是指针,其在堆中存储的是zhangsan这个string在内存中的的地址值
  7. //&strM是指针strM在堆中的地址
  8. NSLog(@"定义前 %p %p", strM, &strM);
  9. void (^myBlock)() = ^ {
  10. /*首先调用block会对strM(指针)进行一份copy,这份copy会在堆中创建
  11. 还有一个指针,这个指针存储的值同strM。都是zhangsan的地址。
  12. 即新copy的指针指向的内容没有变
  13. */
  14. // 注意以下的操作是改动strM指针指向的内容
  15. [strM setString:@"lisi"];
  16. NSLog(@"inblock %p %p", strM, &strM);
  17. //输出:strM没有变。由于存储的都是zhangsan的地址,&strM为堆中新地址
  18. /*
  19. *这句代码是改动指针strM。由于strM copy过来后是仅仅读的,所以同样例2编译会报错,须要在定义strM时加__block
  20. strM = [NSMutableString stringWithString:@"wangwu"];
  21. NSLog(@"inblock %p %p", strM, &strM);
  22. */
  23. };
  24. //大家想想使用__block输出会是什么呢
  25. NSLog(@"定义后 %p %p", strM, &strM);
  26. myBlock();
  27. NSLog(@"%@", strM);
  28. }

上面的样例搞定了,来让我们看下各种类型的变量与block之间的互动:

  1. //演示样例4:各种类型的变量和block之间的互动
  2. extern NSInteger CounterGlobal;
  3. static NSInteger CounterStatic;
  4. NSInteger localCounter = 42 ;
  5. __block char localCharacter;
  6. void (^aBlock)( void ) = ^( void )
  7. {
  8. ++ CounterGlobal ; //能够存取。
  9. ++ CounterStatic ; //能够存取。 
  10. CounterGlobal = localCounter; //localCounter在block 建立时就不可变了。
  11. localCharacter = 'a' ; //设定外面定义的localCharacter 变数。
  12. };
  13. ++localCounter; //不会影响的block 中的值。
  14. localCharacter = 'b' ;
  15. aBlock(); //运行block 的内容。
  16. //运行完后,localCharachter 会变成'a'

(3) block 引用成员变量

OC对象。不同于基本类型。Block会引起对象的引用计数变化。若我们在block中引用到oc的对象。则对象的引用计数器会加1, 只是在对象前 加__block修饰,则參考计数不变。

  1. - 若直接存取实例变量(instance variable),self的參考计数将被加1
  2. - 若透过变量存取实例变量的值,则变量的參考计数将被加1
  3. - 在对象前加 __block 则參考计数不会自己主动加1
  1. //样例1:定义一个变量间接给block调用,成员变量引用计数不变
  2. dispatch_async (queue, ^{
  3. // 由于直接存取实例变量instanceVariable ,所以self 的retain count 会加1
  4. doSomethingWithObject (instanceVariable);
  5. });
  6. //通过
  7. id localVaribale = instanceVariable;
  8. dispatch_async (queue, ^{
  9. //localVariable 是存取值,所以这时仅仅有localVariable 的retain count 加1
  10. //self 的 return count  并不会添加。
  11. doSomethingWithObject (localVaribale);
  12. });

上面仅仅是简单演示下block引用成员变量,以下我们研究下block引用成员变量时出现的一个经典问题:循环引用

在block内部使用成员变量,例如以下:

  1. @interface ViewController : UIViewController
  2. {
  3. NSString *_string;
  4. }
  5. @end

在block创建中:

  1. _block = ^(){
  2. NSLog(@"string %@", self.string);
  3. };

上面代码中block是会对内部的成员变量进行一次retain, 即self会被retain一次。

对于block 使用 成员变量self.string来说。block内部是直接强引用self的。也就是block持有了self,在这里bock又作为self的一个成员被持有,就会导致循环引用和内存泄露

改动方案非常easy:

新建一个__block scope的局部变量,并把self赋值给它,而在block内部则使用这个局部变量来进行取值,上面说过:__block标记的变量是不会被自己主动retain的。

  1. __block ViewController *controller = self;
  2. _block = ^(){
  3. NSLog(@"string %@", controller.string);
  4. };

4. block 基本使用

当block定义完毕后,我们除了能够像使用一般函数的方式来直接调用它以外,还能够有其它妙用。这些灵活的应用才是block最为强大的地方。

(1) block 作为函数參数

我们能够像使用一般函数使用參数的方式将block以函数參数的型式传入函数中,在这样的情况下,大多数我们使用block的方式将不会倾向定义一个block。而是直接以内嵌的方式来将block传入,这也是眼下新版SDK中主流的做法

以下的样例中,block本身就是函数參数的一部分

  1. char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" };
  2. qsort_b (myCharacters, 3 , sizeof ( char *), ^( const void *l, const void *r)
  3. {
  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"}

(2) Block当作方法的參数

  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. {
  10. if ([filterSet containsObject : obj])
  11. {
  12. return YES ;
  13. }
  14. }
  15. return NO ;
  16. };
  17. NSIndexSet *indexes = [array indexesOfObjectsPassingTest :test];
  18. NSLog ( @"indexes: %@" , indexes);
  19. // 结果:indexes: <NSIndexSet: 0x6101ff0>[number of indexes: 4 (in 2 ranges), indexes: (0-1 3-4)]
  20. // 前5笔资料中,有4笔符合条件。它们的索引值各自是0-1, 3-4

(3)OC方法中block实例

A. sortedArrayUsingComparator:

  1. //这里面block代码块直接内嵌作为方法的參数
  2. NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {
  3. //左边大于右边。降序
  4. if ([obj1 integerValue] > [obj2 integerValue])
  5. {
  6. return (NSComparisonResult)NSOrderedDescending;
  7. }
  8. //右边大于左边,升序
  9. if ([obj1 integerValue] < [obj2 integerValue])
  10. {
  11. return (NSComparisonResult)NSOrderedAscending;
  12. }
  13. //同样
  14. return (NSComparisonResult)NSOrderedSame;
  15. }];

B. enumerateObjectsUsingBlock

通常enumerateObjectsUsingBlock: 和 (for(… in …)在效率上基本一致,有时会快些。

主要是由于它们都是基于 NSFastEnumeration实现的。

高速迭代在处理的过程中须要多一次转换。当然也会消耗掉一些时间. 基于Block的迭代能够达到本机存储一样快的遍历集合. 对于字典同样适用。

  • 注意”enumerateObjectsUsingBlock” 改动局部变量时。 你须要声明局部变量为 __block 类型.

  • enumerateObjectsWithOptions:usingBlock: 支持并发迭代或反向迭代,并发迭代时效率也非常高.

  • 对于字典而言, enumerateObjectsWithOptions:usingBlock 也是唯一的方式能够并发实现恢复Key-Value值.

演示样例代码:

  1. //定义一个可变数组
  2. NSMutableArray *test = [NSMutableArray array];
  3. //向数组中加入元素
  4. for (int i= 0; i < 10000; i++)
  5. {
  6. [test addObject:@"i"];
  7. }
  8. //迭代数组输出
  9. [test enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  10. NSLog(@"%@",obj);
  11. }];

5. block内存管理

(1)堆(Stack)和栈(Heap)

heap和stack是内存管理的两个重要概念。

这里指的是内存的分配区域。

  1. stack的空间由操作系统进⾏行分配。

    在现代操作系统中,一个线程会分配⼀个stack. 当一个函数被调用,一个stack frame(栈帧)就会被压到stack里。

    里包括这个函数涉及的參数,局部变量,返回地址等相关信息。当函数返回后,这个栈帧就会被销毁。

    ⽽这一切都是自己主动的,由系统帮我们进行分配与销毁。对于程序猿是透明的,我们不须要手动调度。

    .

  2. heap的空间须要手动分配。 heap与动态内存分配相关,内存能够随时在堆中分配和销毁。

    我们须要明白请求内存分配与内存销毁。 简单来说,就

    是malloc与free.

(2)Objective-C中的Stack和Heap

首先全部的Objective-C对象都是分配在heap的。 在OC经典的内存分配与初始化:

  1. NSObject *obj = [[NSObject alloc] init];

一个对象在alloc的时候,就在Heap分配了内存空间。 stack对象通常有速度的优势,⽽且不会发生内存泄露问题。那么为什么OC的对象都是分配在heap的呢? 原因在于:

  1. stack对象的⽣生命周期所导致的问题。比如一旦函数返回,则所在的stack frame就会被摧毁。那么此时返回的对象也 会一并摧毁。这个时候我们去retain这个对象是无效的。由于整个stack frame都已经被摧毁了。简单⽽言就是stack 对象的⽣命周期不适合Objective-C的引用计数内存管理⽅方法。

    .
  2. stack对象不够灵活,不具备足够的扩展性。创建时⻓度已经是固定的,⽽stack对象的拥有者也就是所在的stack frame

我们知道block 在使用@property定义时,官方建议我们使⽤用copy修饰符

  1. // 定义一个块代码的属性。block属性须要用 copy
  2. @property (nonatomic, copy) void (^completion)(NSString *text);

尽管在ARC时代已经不须要再显式声明了,使用strong是没有问题的,可是仍然建 议我们使⽤copy以显示相关拷贝⾏为。

(3)为什么要使用copy?!

事实上Objective-C是有它的Stack object的。那就是block。

在Objective-C语⾔言中,⼀一共同拥有3种类型的block:

  • _NSConcreteGlobalBlock 全局的静态block,不会訪问不论什么外部变量。
  • _NSConcreteStackBlock 保存在栈中的block,当函数返回时会被销毁。
  • _NSConcreteMallocBlock 保存在堆中的block,当引⽤用计数为0时会被销毁。

这⾥我们主要基于内存管理的角度对它们进行分类。

  1. NSConcreteGlobalBlock,这样的不捕捉外界变量的block是不须要内存管理的,这样的block不存在于Heap或是Stack⽽是作为代码片段存在,相似于C函数。
  2. NSConcreteStackBlock,须要涉及到外界变量的block在创建的时候是在stack上⾯分配空间的,也就是⼀旦所在函数返回,运行弹栈,则会被摧毁。这就导致内存管理的问题,假设我们希望保存这个block或者是返回它,假设没有做进⼀步的copy处理,则必定会出现故障。

举个栗子,在手动管理引⽤计数时,假设在exampleD_getBlock方法返回block 时没有运行[[block copy] autorelease]的操作,则方法运行完毕后,block就会被销毁, 返回block是无效的。

  1. //定义了一个block
  2. typedef void (^dBlock)();
  3. dBlock exampleD_getBlock() {
  4. char d = 'D';
  5. return ^{
  6. printf("%c\n", d);
  7. };
  8. }
  9. void exampleD()
  10. {
  11. exampleD_getBlock();
  12. }
  1. NSConcreteMallocBlock,因此为了解决block作为Stack object的这个问题,我们终于须要把它拷⻉到堆上来。

复制到堆后,block的⽣命周期就与⼀般的OC对象⼀样了,我们通过引用计数来对其进行内存管理。

如今我们知道为么么要Copy了吧-_-

block在创建时是stack对象,假设我们须要在离开当前函数仍能够使用我们创建的block。我们就须要把它 拷⻉到堆上以便进行以引用计数为基础的内存管理。

在ARC模式下,系统帮助我们完毕了copy的⼯作。

在ARC下,即使你声明的修饰符是strong,实际上效果是与声明为copy一样的。

因此在ARC情况下,创建的block仍然是NSConcreteStackBlock类型,仅仅只是当block被引用或返回时,ARC帮助我们完毕了copy和内存管理的工作。

总结

在ARC下,我们能够将block看做⼀一个正常的OC对象,与其它对象的内存管理没什么不同。

MRC下要使用 Block_copy()和 Block_release 来管理内存。


(4)再来一个栗子

上面讲到ARC下, block在被引用或返回时类型会由NSConcreteStackBlock转换为 NSConcreteHeapBlock,那在MRC环境下该怎么办呢。

block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。

我们在viewDidLoad中创建一个_block:

  1. - (void)viewDidLoad
  2. {
  3. [superviewDidLoad];
  4. int number = 1;
  5. _block = ^(){
  6. NSLog(@"number %d", number);
  7. };
  8. }

而且在一个button的事件中调用了这个block:

  1. - (IBAction)testDidClick:(id)sender {
  2. _block();
  3. }

此时假设按了button之后就会导致程序崩溃。解决问题的方法非常easy

在创建完block的时候须要调用 Block_copy函数。它会把block从栈上移动到堆上。那么就能够在其它地方使用这个block了。

Block_copy实际上是一个宏。例如以下:

  1. #define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))

使用后,使用 Block_release。从堆中释放掉

改动代码例如以下:

  1. _block = ^(){
  2. NSLog(@"number %d", number);
  3. };
  4. _block = Block_copy(_block);

同理。特别须要注意的地方就是在把block放到集合类当中去的时候,假设直接把生成的block放入到集合类中,是无法在其它地方使用block。必须要对block进行copy。

示比例如以下:

  1. [array addObject:[[^{ NSLog(@"hello!"); } copy] autorelease]];

Q:为什么不使用简单的copy方法 而是 Blockcopy呢?

由于blcok是复杂的匿名函数。简单的copy在有些时候不能实现准确的copy,具体就要看各自的C源代码了

6. 视图控制器反向传值

使用Block的地方非常多,当中传值仅仅是当中的一小部分,以下介绍Block在两个界面之间的传值:

先说一下思想:

首先,创建两个视图控制器,在第一个视图控制器中创建一个UILabel和一个UIButton,当中UILabel是为了显示第二个视图控制器传过来的字符串,UIButton是为了push到第二个界面。

第二个界面的仅仅有一个UITextField,是为了输入文字,当输入文字,而且返回第一个界面的时候,当第二个视图将要消失的时候,就将第二个界面上TextFiled中的文字传给第一个界面,而且显示在UILabel上。

事实上核心代码就几行代码:

在第二个视图控制器的.h文件里定义声明Block属性

  1. typedef void (^ReturnTextBlock)(NSString *showText);
  2. @interface TextFieldViewController : UIViewController
  3. @property (nonatomic, copy) ReturnTextBlock returnTextBlock;
  4. - (void)returnText:(ReturnTextBlock)block;
  5. @end
  1. 第一行代码是为要声明的Block又一次定义了一个名字
  2. ReturnTextBlock
  3. 这样,以下在使用的时候就会非常方便。
  4. 第三行是定义的一个Block属性
  5. 第四行是一个在第一个界面传进来一个Block语句块的函数,不用也能够,只是加上会降低代码的书写量

实现第二个视图控制器的方法

  1. - (void)returnText:(ReturnTextBlock)block {
  2. self.returnTextBlock = block;
  3. }
  4. - (void)viewWillDisappear:(BOOL)animated {
  5. if (self.returnTextBlock != nil) {
  6. self.returnTextBlock(self.inputTF.text);
  7. }
  8. }

当中inputTF是视图中的UITextField。

第一个方法就是定义的那个方法,把传进来的Block语句块保存到本类的实例变量returnTextBlock(.h中定义的属性)中,然后寻找一个时机调用,而这个时机就是上面说到的,当视图将要消失的时候。须要重写:

  1. - (void)viewWillDisappear:(BOOL)animated;
  2. 方法。

在第一个视图中获得第二个视图控制器。而且用第二个视图控制器来调用定义的属性

例如以下:

  1. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
  2. {
  3. // Get the new view controller using [segue destinationViewController].
  4. // Pass the selected object to the new view controller.
  5. TextFieldViewController *tfVC = segue.destinationViewController;
  6. [tfVC returnText:^(NSString *showText) {
  7. self.showLabel.text = showText;
  8. }];
  9. }

能够看到代码中的凝视,系统告诉我们能够用

  1. [segue destinationViewController]

来获得新的视图控制器,也就是我们说的第二个视图控制器。

这时候上面(第一步中)定义的那个方法起作用了,假设你写一个[tfVC return Text按回车 ,系统会自己主动提示出来一个:

  1. tfVC returnText:<#^(NSString *showText)block#>

的东西。我们仅仅要在焦点上回车,就能够高速创建一个代码块了,大家能够试试。这在写代码的时候是非常方便的。

面试题:

  1. __block什么时候用

当须要在block 中改动外部变量时使用,当须要訪问内部成员变量时。

2.在block里面, 对数组运行加入操作, 这个数组须要声明成 __block吗?

当然不须要,由于数组能够理解为指针,在block中对数组进行加入操作。仅仅是改变了指针指向的值,而没有改动外部数组地址。具体參见block訪问成员变量演示样例3

3.在block里面, 对NSInteger进行改动, 这个NSInteger是否须要声明成__blcok

必须须要,NSInteger -> typedef long NSInteger; 这货披着OC的外衣,事实上就是一个基本类型。基本类型在没有static 等的保护下,当然须要__block

  1. 悄悄告诉你哦,blockiOS的面试中是非常重要的,假设你能把上面解说的内容理解了。那么就仰天长啸出门去了。

參考

  1. http://www.cocoachina.com/ios/20120514/4247.html

    使用block开发遇到的问题
  2. http://blog.csdn.net/hherima/article/details/3858610

    这篇block 博客系列从C源代码的角度具体分析了blcok原理

  3. http://mobile.51cto.com/iphone-446829.htm

  4. http://blog.devtang.com/blog/2013/07/28/a-look-inside-blocks/

    巧叔的谈Objective-C Block的实现

版权声明:本文博主原创文章,博客,未经同意不得转载。

有趣 IOS 开展 - block 使用具体解释的更多相关文章

  1. iOS 中Block以及Blocks的使用,闭包方法调用

    OC: -(void)dataWithUrl:(NSString*)string AndId:(NSInteger)id returnName:(void(^)(NSString*name))back ...

  2. iOS中block的用法 以及和函数用法的区别

    ios中block的用法和函数的用法大致相同 但是block的用法的灵活性更高: 不带参数的block: void ^(MyBlock)() = ^{}; 调用的时候  MyBlock(); 带参数的 ...

  3. iOS开发--Block

    iOS开发--Block 1.什么是Block,block 的作用 ui开发和网络常见功能实现回调,按钮的事件处理方法是回调方法以及网络下载后的回调处理 (1)按钮 target-action   一 ...

  4. iOS之block

    1. Block的声明和线程安全Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非ARC ...

  5. iOS开发——Block详解

    iOS开发--Block详解 1. Block是什么 代码块 匿名函数 闭包--能够读取其他函数内部变量的函数 函数变量 实现基于指针和函数指针 实现回调的机制 Block是一个非常有特色的语法,它可 ...

  6. ios开发 block语句块

    ios开发 block语句块 1.block 理解为匿名函数 2.block变量的定义 //定义block变量,^表示定义block //技巧:函数名左右加括号,在函数名前面在加^ void (^bl ...

  7. iOS中Block介绍(一)基础

    ios开发block的使用指南,以及深入理解block的内存管理,也适用于osx开发.讨论范围:block的使用,内存管理,内部实现.不包含的内容:gc arc下的block内存,block在c++中 ...

  8. iOS中Block介绍 基础

    ios开发block的使用指南,以及深入理解block的内存管理,也适用于osx开发.讨论范围:block的使用,内存管理,内部实现.不包含的内容:gc arc下的block内存,block在c++中 ...

  9. iOS中block类型大全

    iOS中block类型大全 typedef的block 作为属性的block 作为变量的block 作为方法变量入参的block 作为方法参数的block 无名block 内联函数的block 递归调 ...

随机推荐

  1. htc one x刷机记录

    这几天有些空余时间都用来刷htc one x,来说说刷机的艰难史吧. 首先是利用百度云rom刷机,本来一直用小米系统,突然发现百度云也能够搞个,所以心血来潮要刷个百度云,先利用软件解锁,哪知道没细致看 ...

  2. PHP+Mysql————表单数据插入数据库及数据提取

    站点在进行新用户注冊时,都会将用户的注冊信息存入数据库中,须要的时候再进行提取.今天写了一个简单的实例. 主要完毕下面几点功能: (1)用户进行注冊,实现password反复确认,验证码校对功能. ( ...

  3. 通常编译亲测56Y国际象棋源代码,精仿56Y国际象棋完整的源代码下载!

    今天公布亲测通常应编译56Y国际象棋源代码,精仿56Y牌源代码.喜欢的能够拿去研究.论坛资源太多.我们会把好的资源都公布出来,同一时候欢迎很多其它的程序猿增加我们! 增加我们的共同学习交流!     ...

  4. Debug with Eclipse

    In this post we are going to see how to develop applications using Eclipse and Portofino 4. The trad ...

  5. swift笔记 (三) —— 字符和字符串

    字符串和字符 苹果要是不提供了unicode的字符串和字符,那就是他们公司全部人的脑袋都被门夹过 他自己家都要发非常多国家的版本号的软件,怎么可能不用unicode呢 此处略去30字... 这里能够拿 ...

  6. Visual C++学习笔记1:一定要注意ANSI和UNICODE差额

    最近的研究VC++.下载VS2013,根据<Visual C++开发实战系列>首先hello我写了一个常规样品,结果显示乱码编辑框.夜已经折腾型转变.然后总结很明显ANSI和UNICODE ...

  7. Android TextView里直接显示图片的三种方法

    方法一:重写TextView的onDraw方法,也挺直观就是不太好控制显示完图片后再显示字体所占空间的位置关系.一般假设字体是在图片上重叠的推荐这样写.时间关系,这个不付源代码了. 方法二:利用Tex ...

  8. LoadRunner获取一个独特的价值在执行的场景

    /* * * 笔者:古柏涛 * 迄今:2015-2-26 * E-mail:gubotao@foxmail.com * * 内容概要: * 本代码产生一个从1970年1月1日0时開始累计以毫秒为单位的 ...

  9. SqlServer表EXCEL数据复制的另一种方法

    一个.SqlServer表中的数据复制到excel 1.新建查询,用sql语句把表数据读出来 2.然后,选择数据,右键.复制(也能够点击连同标题复制),拷贝到记事本中(不然会乱码) 3.然后再把记事本 ...

  10. [原创] 使用rpi + crontab + git 定时向bitbucket 推送 照片

    #前提条件,你得有一个bitbucket的帐户 1.定时启动脚本代码 使用的是crontab Cronfile 内容如下: 此文件的意思是,每隔10分钟,就运行一次 /home/pi/Rpi_uplo ...