2015 IOS 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:
   2: void (^blockReturningVoidWithVoidArgument)( void );
   3:
   4: int   (^blockReturningIntWithIntAndCharArguments)( int , char );
   5:
   6: void (^arrayOfTenBlocksReturningVoidWinIntArgument[ 10 ])( int );
   7: X.3.2 建立一个Block
   8: 
   9: 我们使用「^」来开始一个block,并在最后使用「;」来表示结束,下面的范例示范了一个block变数,然后再定义一个block把它指定给block变数:
  10: 
  11: int (^oneFrom)( int );
  12:
  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: }
时间: 2024-10-14 11:55:00

2015 IOS Block——在蓝懿教育的相关文章

2015 IOS TableView ——在蓝懿教育 学习笔记

TableView   蓝懿教育 1首先创建 UITableView *tableView = [[UITableView alloc]initWithFrame:self.view.bounds]; tableView.dataSource = self; //dataSource 是数据源 [self.view addSubview:tableView]; 2创建协议 @interface ViewController ()<</span>UITableViewDataSource&

2015 IOS 字典——在蓝懿教育 学习笔记

字典 NSMutableDictionary *dic = [NSM.. dictiongaryWithobject :@”  “ forKey:@“  “]: (实现了NSCopying的任意对象) dic setobject :@“” forKey :@“” .. @[  ]————数组的简写 注意 :字典是无序的. 创建页面跳转(用字典): 把vc 的字典搬到tvc 1.创建数组(不可变) 字典声明成属性 2.dic此时变为self.dic 3.行数self.dic.count 4.在行数

2015 IOS 地图——在蓝懿教育 学习笔记

1,在VC中添加地图界面MKMapView(全屏) 2.选工程界面相对应的界面中倒数第二行,Build Phases选项中 选择第三个 的+号,弹出来搜索框输入mapk,选择搜索结果确认添加,此时地图能显示出来. (3.在SB中的地图右侧选项有三个模式,(二维线路,卫星无线路,卫星有线路)) 4.关联代码在vc中关联myMV.但此时并不被系统识别,此时要添加import<MapKit/MapKit.h>协议,此时就被识别了 5.此时要从网络获取到经纬度,比如天安门经纬度, 此时要用到方法——创

2015 IOS 学习笔记 控 件 ——蓝懿教育

UIlabel  *l1   蓝懿教育 text font 设置字体 设置居中  l1 setTextAlignment:NSTextAlignmentCenter 设置行数  numberOfLines //    lf.font = [UIFont fontWithName:@"字体名称" size:18]; //设置居中 [l setTextAlignment:NSTextAlignmentCenter]; UIbutton 内容  setTitle    UI button  

2015 IOS 制作通讯录——在蓝懿教育 学习笔记

来自蓝懿教育 学生笔记 1.删除界面中的vc,并且建立一个tablevc,在sb中创建navigation,并且关联TableView中的属性,并让箭头指向第一页面的navigation,并关联cell 2.拖拽界面view controller 建立一个personviewcontroller使之与sb中的关联, 3.在tablevc中建立左上角的加号 4.关联Personvc属性,并且在左上角加号中的事件方法中写入事件 5.删除vdidload中的多余行(留下添加右上角删除按钮代码): 6.

2015 IOS 学习笔记 正向反向练习 ——蓝懿教育

正向传值   蓝懿教育 跳转页面过程中 从ViewController(A类) 向 SecondViewController(B类)中跳转 在ViewControlller中创建的一个SecondViewController页面 所以从ViewController向Second传叫正向传值 如果传的是text 那么实际传的是字符串 所以要先在 SecondViewController.h中 建一个属性 Nsstring*accept    .h是开放可见的 那么ViewControlller中

2015 IOS 文件管理器 ——在蓝懿教育 学习笔记

得到文件管理器   蓝懿教育 NSFileManager *fm = [NSFileManager defaultManager]; //    复制 //    [fm copyItemAtPath:@"/Users/ivan/Desktop/a.png" toPath:@"/Users/ivan/Desktop/copy/a.png" error:nil]; //    移动  移动的时候目的地不能有重名的 //    [fm moveItemAtPath:@&

蓝懿教育 内存管理

栈区:里面存放基本数据类型及对象地址,不需要考虑内存释放问题 堆区:由程序员分配释放.若程序员不释放,则这块内存永远不会被释放,会造成内存泄露.iOS中,alloc创建出来对象都放在堆区. alloc 命令会导致内存计数+1 谁加的谁负责 属性括号里面的的关键字 用来控制set和get方法中的内容. retain(strong) 会让set方法中做两件事1.把原来的值release 2.把新传递进来的值 retain assign(weak)  会让set方法中只做一件事 就是把传递进来的值赋值

蓝懿教育 零基础学习iOS10天的一些心得

10天以前来到蓝懿开始我的iOS学习生涯,获取知识的时候时间过得很快,10天已经过去了,从开始的时候对iOS这个概念都一知半解,到现在能自己摸索着试着写出一些简单的小游戏小项目,像简单版的计算器.打砖块,接水果,微信飞机大战等,并在里面加些自己的想法,虽然编写代码的过程中时常会有些错误,但才刚开始学习,出错在所难免,多多练习.思考,从错误中汲取知识才是最重要的,同时感觉自己这一周的时候收获很多. 对于学习知识或者一项技能,学习方法和心态是很重要,在学习当中应该保持一颗良好的心态.应该借鉴别人好的