Block
Apple 在C, Objective-C, C++加上Block这个延申用法。目前只有Mac 10.6 和iOS 4有支持。Block是由一堆可执行的程式组成,也可以称作沒有名字的Function (Anonymous function)。如果是Mac 10.6 或 iOS 4.0 之前的平台可以利用 http://code.google.com/p/plblocks/ 这个project得以支持Block语法。
Apple有一个叫做GCD(Grand Central Dispach)的新功能,用在同步处理(concurrency)的环境下有更好的效率。Block语法产生的动机就是来自于GCD,用Block包好 一个工作量交給GCD,GCD有一个宏观的视野可以来分配CPU,GPU,Memory的来下最好的決定。
Block 簡介
Block其实行为和Function很像,最大的差別是在可以存取同一个Scope的变数值。
Block 实体会长成这样
Block实体开头是"^",接著是由小括号所包起来的参数列(比如 int a, int b, float c),行为的主体由大括号包起来,专有名词叫做block literal。行为主体可以用return回传值,型別会被compiler自动辨认出来。如果沒有參數列要这样写(void)。
看个列子
这是代表Block会回传輸入值的平方值(int a 就是參數列,return a*a; 就是行为主体)。记得主体里最后要加";"因为是敘述,而整个{}最后也要要加";"因为Block是个物件实体。
用法就是
很怪吧。后面小括号里的5会被当成a的输入值然后经由Block输出5*5 = 25指定給result这个变数。
有沒有简单一点的方法不然每次都要写这么长?有。接下来要介紹一个叫Block Pointer的东西来简化我们的写法。
Block Pointer是这样宣告的
直接来看一个列子
使用Block Pointer的例子
也可以把Block Pointer当成参数传給一个function,比如说
传入一个有一个int输入和int输出的Block 型別的参数
呼叫这个myFunction的时候就是这样呼叫
或是不用block pointer 直接给一个block 实体,就这样写
当成Objective-C method 的传入值的话都是要把型別写在变数前面然后加上小括號,因些应该就要这样写
让文至此是不是對Block有基本的认识? 接下来我们要谈谈Block相关的行为和特色
首先是来看一下在Block执面存取外部变数的方法
存取变数
1. 可以让取和Block pointer同一个scope的变数值:
我们再来看一个很有趣的例子
事实上呢,myPtr在其主体用到outA这个变数值的时候是做了一个copy的动作把outA的值copy下来。所以之后outA即使换了新的值对于myPtr执copy的值是沒有影响到的。
要注意的是,这个指的值是变数的值,如果这个变数的值是一个记忆体的位置,换句话说,这个变数是个pointer的话,它指到的值是可以在block执被改变的。
原本mutableArray的值是{@"one",@"two",@"three"}在block执被更改mutableArray所指向的物件后,mutableArray的值就会被成{@"one",@"two"}
2. 直接存取static 的变数
甚至可以在block执面直接改变outA的值比如这样写
3. Block Variable
在某个变数前面如果加上修饰字__block 的话(注意block前有两个下底线),这个变数又称为block variable。那么在block执就可以任意修改此变数值,变数值的改变也可以知道。
因为myPtr和myPtr2都有用到num这个block variable,最后result的值就会是7
生命周期和记忆体管理
因为block也是继承自NSObject,所以其生命周期和记忆体的管理也就非常之重要。
block一開始都是被放到stack执,换句话说其生命周期隨著method或function结束就会被回收,和一般变数的生命周期一样。
关于记忆体的管理请遵循这幾个要点
1. block pointer的实体会在method或function结束后就会被清掉
2. 如果要保存block pointer的实体要用-copy指令,这样block pointer就会被放到heap执
2.1 block 主体执用到的block variable 也会被搬到heap 而有新的记忆体位置,且一并更新有用到这个block variable 的block都指到新的位置
2.2 一般的variable值会被copy
2.3 如果主体执用到的variable是object的话,此object会被retain, block release时也会被release
2.4 __block variable 执用到的object是不会被retain的
首先来看一下这个例子
此程式由genBlock执產生的block再指定給main function的outBlock变数,执行这个程式会得到
Segmentation fault
(注:有时候把 genBlock执的a 去掉就可以跑出结果的情形,这是系統cache住记忆体,并不是inBlock真得一直存在,久了还是会被回收,千萬不要以为是對的写法)
表示我们用到了不该用的记忆体,在这个例子的情況下是在genBlock执的inBlock变数在return的时候就被回收了,outBlock无法有一个合法的记忆体位置-retainCount就沒意义了。
如果这个时候需要保留inBlock的值就要用-copy指令,将genBlock改成
这样[inBlock copy]的回传值就会被放到heap,就可以一直使用(记得要release)
执行结果是
再次提醒要记得release outBlock。
如果一回传[inBlock copy]的值就不再需要的时候可以这样写
-copy指令是为了要把block 从stack搬到heap,autorelease是为了平衝retainCount加到autorelease oop ,回传之后等到事件结束就清掉。
接下来是block存取到的local variable是个物件的型別,然后做copy 指令时
结果会印出
这个结果和上面2.3提到的一样,local variable被retain了
那再来试试2.4,在local variable前面加上__block
执行的结果就是会
Block Copying注意事项
如果在Class method执面做copying block动作的话
1. 在Block执如果有直接存取到self,则self会被retain
2. 在Block执如果取存到instance variable (无论直接或是从accessor),则self会被retain
3. 取存到local variable所拥有的object时,这个object会被retain
让我们来看一个自定的Class
在main 执使用如下
其执行的结果为
因为在MyObject的建构子执myLog这个block pointer用了title这个instance variable然后就会retain self也就是MyObject的物件。
尽量不要这样写,会造成retain cycle,改善的方法是把建构子改成这样
在Block主体执用newTitle这个变数而不是title。这样self就不会被retain了。
最后谈一个小陷井
这样很可能就会当掉了,因为myLog 实体在if 或是else结束后就被清掉了。要记得。
要用copy来解決这个问题,但要记得release。
文章出处不明
Apple 在C, Objective-C, C++加上Block这个延申用法。目前只有Mac 10.6 和iOS 4有支持。Block是由一堆可执行的程式组成,也可以称作沒有名字的Function (Anonymous function)。如果是Mac 10.6 或 iOS 4.0 之前的平台可以利用 http://code.google.com/p/plblocks/ 这个project得以支持Block语法。
Apple有一个叫做GCD(Grand Central Dispach)的新功能,用在同步处理(concurrency)的环境下有更好的效率。Block语法产生的动机就是来自于GCD,用Block包好 一个工作量交給GCD,GCD有一个宏观的视野可以来分配CPU,GPU,Memory的来下最好的決定。
Block 簡介
Block其实行为和Function很像,最大的差別是在可以存取同一个Scope的变数值。
Block 实体会长成这样
引用
^(传入参数列) {行为主体};
Block实体开头是"^",接著是由小括号所包起来的参数列(比如 int a, int b, float c),行为的主体由大括号包起来,专有名词叫做block literal。行为主体可以用return回传值,型別会被compiler自动辨认出来。如果沒有參數列要这样写(void)。
看个列子
^(int a) {return a*a;};
这是代表Block会回传輸入值的平方值(int a 就是參數列,return a*a; 就是行为主体)。记得主体里最后要加";"因为是敘述,而整个{}最后也要要加";"因为Block是个物件实体。
用法就是
int result = ^(int a) {return a*a;} (5);
很怪吧。后面小括号里的5会被当成a的输入值然后经由Block输出5*5 = 25指定給result这个变数。
有沒有简单一点的方法不然每次都要写这么长?有。接下来要介紹一个叫Block Pointer的东西来简化我们的写法。
Block Pointer是这样宣告的
引用
回传值 (^名字) (参数列);
直接来看一个列子
int (^square) (int); // 有一个叫square的Block Pointer,其所指向的Block是有一个int 输入和 int 输出
square = ^(int a ) {return a*a ;}; // 将刚刚Block 实体指定給 square
使用Block Pointer的例子
int result = square(5); // 感觉上不就是funtion的用法吗?
也可以把Block Pointer当成参数传給一个function,比如说
void myFuction( int (^mySquare) (int) ); // function 的宣告,
传入一个有一个int输入和int输出的Block 型別的参数
呼叫这个myFunction的时候就是这样呼叫
int (^mySqaure) (int) = ^(int a) {return a*a;}; // 先給好一个有实体的block pointer叫mySquare
myFunction( mySqaure ) ; //把mySquare这个block pointer给myFunction这个function
或是不用block pointer 直接给一个block 实体,就这样写
myFunction( ^(int a) {return a*a} ) ;
当成Objective-C method 的传入值的话都是要把型別写在变数前面然后加上小括號,因些应该就要这样写
-(void) objcMethod:( int (^) (int) ) square; // square 变数的型別是 int (^) (int)
让文至此是不是對Block有基本的认识? 接下来我们要谈谈Block相关的行为和特色
首先是来看一下在Block执面存取外部变数的方法
存取变数
1. 可以让取和Block pointer同一个scope的变数值:
{ int outA = 8; int (^myPtr) (int) = ^(int a) {return outA+a;}; // block 执面可以让同一个scope的outA的值 int result = myPtr(3); // result is 11 }
我们再来看一个很有趣的例子
{ int outA = 8; int (^myPtr) (int) = ^(int a) {return outA+a;}; // block 执面可以让同一个scope的outA的值 outA = 5; // 在呼叫myPtr之前改变outA的值 int result = myPtr(3); // result 的值还是 11并不是 8 }
事实上呢,myPtr在其主体用到outA这个变数值的时候是做了一个copy的动作把outA的值copy下来。所以之后outA即使换了新的值对于myPtr执copy的值是沒有影响到的。
要注意的是,这个指的值是变数的值,如果这个变数的值是一个记忆体的位置,换句话说,这个变数是个pointer的话,它指到的值是可以在block执被改变的。
{ NSMutableArray * mutableArray = [NSMutableArray arrayWithObjects:@"one",@"two",@"three",nil]; int result = ^(int a) { [mutableArray removeLastObject]; return a*a;} (5); NSLog(@"test array %@", mutableArray); }
原本mutableArray的值是{@"one",@"two",@"three"}在block执被更改mutableArray所指向的物件后,mutableArray的值就会被成{@"one",@"two"}
2. 直接存取static 的变数
{ static int outA = 8; int (^myPtr) (int) = ^(int a) {return outA+a;}; // block 执面可以让同一个scope的outA的值 outA = 5; // 在呼叫myPtr之前改变outA的值 int result = myPtr(3); // result 的值是 8,因为outA是个static 变数会直接反应其值 }
甚至可以在block执面直接改变outA的值比如这样写
{ static int outA = 8; int (^myPtr) (int) = ^(int a) { outA= 5; return outA+a;}; // block 执面改变outA的值 int result = myPtr(3); // result 的值是 8,因为outA是个static 变数会直接反应其值 }
3. Block Variable
在某个变数前面如果加上修饰字__block 的话(注意block前有两个下底线),这个变数又称为block variable。那么在block执就可以任意修改此变数值,变数值的改变也可以知道。
{ __block int num = 5; int (^myPtr) (int) = ^(int a) { return num++;}; int (^myPtr2) (int) = ^(int a) { return num++;}; int result = myPtr(0); result = myPtr2(0); }
因为myPtr和myPtr2都有用到num这个block variable,最后result的值就会是7
生命周期和记忆体管理
因为block也是继承自NSObject,所以其生命周期和记忆体的管理也就非常之重要。
block一開始都是被放到stack执,换句话说其生命周期隨著method或function结束就会被回收,和一般变数的生命周期一样。
关于记忆体的管理请遵循这幾个要点
1. block pointer的实体会在method或function结束后就会被清掉
2. 如果要保存block pointer的实体要用-copy指令,这样block pointer就会被放到heap执
2.1 block 主体执用到的block variable 也会被搬到heap 而有新的记忆体位置,且一并更新有用到这个block variable 的block都指到新的位置
2.2 一般的variable值会被copy
2.3 如果主体执用到的variable是object的话,此object会被retain, block release时也会被release
2.4 __block variable 执用到的object是不会被retain的
首先来看一下这个例子
typedef int (^MyBlock)(int); MyBlock genBlock(); int main(){ MyBlock outBlock = genBlock(); int result = outBlock(5); NSLog(@"result is %d",[outBlock retainCount] ); // segmentation fault NSLog(@"result is %d",result ); return 0 ; } MyBlock genBlock() { int a = 3; MyBlock inBlock = ^(int n) { return n*a; }; return inBlock ; }
此程式由genBlock执產生的block再指定給main function的outBlock变数,执行这个程式会得到
Segmentation fault
(注:有时候把 genBlock执的a 去掉就可以跑出结果的情形,这是系統cache住记忆体,并不是inBlock真得一直存在,久了还是会被回收,千萬不要以为是對的写法)
表示我们用到了不该用的记忆体,在这个例子的情況下是在genBlock执的inBlock变数在return的时候就被回收了,outBlock无法有一个合法的记忆体位置-retainCount就沒意义了。
如果这个时候需要保留inBlock的值就要用-copy指令,将genBlock改成
MyBlock genBlock() { int a = 3; MyBlock inBlock = ^(int n) { return n*a; }; return [inBlock copy] ; }
这样[inBlock copy]的回传值就会被放到heap,就可以一直使用(记得要release)
执行结果是
引用
result is 1
result is 15
result is 15
再次提醒要记得release outBlock。
如果一回传[inBlock copy]的值就不再需要的时候可以这样写
MyBlock genBlock() { int a = 3; MyBlock inBlock = ^(int n) { return n*a; }; return [[inBlock copy] autorelease] ; }
-copy指令是为了要把block 从stack搬到heap,autorelease是为了平衝retainCount加到autorelease oop ,回传之后等到事件结束就清掉。
接下来是block存取到的local variable是个物件的型別,然后做copy 指令时
MyBlock genBlock() { int a = 3; NSMutableString * myString = [NSMutableString string]; MyBlock inBlock = ^(int n) { NSLog(@"retain count of string %d",[myString retainCount]); return n*a; }; return [inBlock copy] ; }
结果会印出
引用
retain count of string 2
这个结果和上面2.3提到的一样,local variable被retain了
那再来试试2.4,在local variable前面加上__block
MyBlock genBlock() { int a = 3; __block NSMutableString * myString = [NSMutableString string]; MyBlock inBlock = ^(int n) { NSLog(@"retain count of string %d",[myString retainCount]); return n*a; }; return [inBlock copy] ; }
执行的结果就是会
引用
retain count of string 1
Block Copying注意事项
如果在Class method执面做copying block动作的话
1. 在Block执如果有直接存取到self,则self会被retain
2. 在Block执如果取存到instance variable (无论直接或是从accessor),则self会被retain
3. 取存到local variable所拥有的object时,这个object会被retain
让我们来看一个自定的Class
@interface MyObject : NSObject { NSString * title; void (^myLog) (NSString * deco); } -(void) logName; @end @implementation MyObject -(id) initWithTitle:(NSString * ) newTitle{ if(self = [super init]){ title = newTitle; myLog = [^(NSString * deco) { NSLog(@"%@%@%@",deco, title, deco );} copy]; } return self; } -(void) logName{ myLog(@"=="); } -(void ) dealloc{ [myLog release]; [title release]; [super dealloc]; } @end
在main 执使用如下
MyObject * mObj = [[MyObject alloc] initWithTitle:@"Car"]; NSLog(@"retainCount of MyObject is %d",[mObj retainCount] ); [mObj logName];
其执行的结果为
引用
retainCount of MyObject is 2
==Car==
==Car==
因为在MyObject的建构子执myLog这个block pointer用了title这个instance variable然后就会retain self也就是MyObject的物件。
尽量不要这样写,会造成retain cycle,改善的方法是把建构子改成这样
-(id) initWithTitle:(NSString * ) newTitle{ if(self = [super init]){ title = newTitle; myLog = [^(NSString * deco) { NSLog(@"%@%@%@",deco, newTitle, deco );} copy]; } return self; }
在Block主体执用newTitle这个变数而不是title。这样self就不会被retain了。
最后谈一个小陷井
void (^myLog) (void); BOOL result ; if(result) myLog = ^ {NSLog(@"YES");}; else myLog = ^ {NSLog(@"NO");}; myLog();
这样很可能就会当掉了,因为myLog 实体在if 或是else结束后就被清掉了。要记得。
要用copy来解決这个问题,但要记得release。
文章出处不明
相关推荐
在这个名为"The Objective-C block utilities you always wish you had"的压缩包中,很显然,它包含了一些用于增强Objective-C中Block功能的实用工具。Blocks是Objective-C中的一个强大特性,它们允许程序员定义可...
书中还可能涵盖Block(代码块)的使用,这是Objective-C中一种强大的语法特性,常用于多线程和异步编程,因为它可以捕获和封装变量的上下文。另外,可能会讲解到Cocoa Touch和Cocoa框架中的线程安全类和方法,以及...
1. **Objective-C基础**:Objective-C是在C语言基础上扩展的,添加了面向对象特性。它的基础包括类、对象、消息传递等概念。类定义了对象的属性和行为,对象则是类的实例。消息传递是Objective-C的核心,通过`...
Objective-C是在C语言的基础上扩展了Smalltalk式的面向对象特性,使得它既有C语言的强大功能,又具备了面向对象编程的灵活性。 在Objective-C中,类是所有对象的基础。类定义了一组属性(实例变量)和方法(函数)...
Objective-C是C语言的超集,它扩展了C语言,加入了面向对象的特性。面向对象编程(OOP)的核心概念包括类、对象、继承、封装和多态。Objective-C通过消息传递机制实现了这些概念,使得代码更加模块化和可维护。 2....
7. **Block和GCD**:Block是Objective-C中的匿名函数,可以用于简化回调和并发处理。Grand Central Dispatch(GCD)是Apple提供的多线程解决方案,书中可能讲解如何利用它们优化程序性能。 8. **Cocoa Touch和App...
1. Objective-C语言简介:Objective-C是一种通用的编程语言,其被广泛应用于苹果公司的macOS、iOS、watchOS和tvOS操作系统。它基于C语言,并加入了Smalltalk风格的消息传递机制。 2. 类和消息机制:Objective-C语言...
Objective-C2.0是苹果公司开发的一种面向对象的编程语言,它是C语言的超集,融合了Smalltalk的动态特性,广泛应用于iOS和Mac OS X操作系统。本资料集中的"Objective-C2.0程序设计习题官方答案"显然是针对学习...
C++11也引入了类似的概念,但Objective-C的Block在语法和使用上有所不同。 6. Foundation框架:Objective-C的基础库Foundation框架提供了许多核心的类和功能,如NSArray、NSDictionary等容器类,以及NSString、...
还可能讲解了Objective-C的特殊特性,如类别(Category)、协议(Protocol)和块(Block)。 2. **语法结构**:Objective-C的语法有其独特性,比如它的方法定义和调用方式,以及在C语言基础上添加的“@”符号,如@...
- **块(Block)**:块是一种匿名函数,在Objective-C中常用于回调函数或者异步操作的完成处理。 #### 应用场景 - **iOS应用开发**:Objective-C是开发iOS应用的主要语言之一,尤其适合于那些需要高性能和复杂用户...
Block(块)是Objective-C的另一项重要特性,它是一种内联函数,可以捕获并存储其定义时的作用域内的变量。Block常用于异步操作的回调,如网络请求或GCD(Grand Central Dispatch)。 Foundation框架是Objective-C...
10. **Block(闭包)**:Objective-C支持块语法,`FractionDemo`可能包含使用块作为参数或返回值的方法,用于实现异步操作或回调。 这些只是Objective-C编程中的一部分核心概念,实际的`FractionDemo`代码会根据...
1. **语法特性**:Objective-C是在C语言的基础上扩展的,保留了C的语法特性,同时引入了消息传递机制,使得它具有面向对象的能力。 2. **类和对象**:Objective-C中的所有数据结构都是基于类的,类是对象的模板,而...
首先,Objective-C是在C语言的基础上扩展的,因此,理解C语言的基本语法是学习Objective-C的前提。它引入了消息传递机制,这是Objective-C的关键特性,允许对象之间进行通信。消息传递类似于函数调用,但更加灵活,...
Objective-C是一种结合了C语言特性和面向对象编程思想的语言,它是苹果iOS和macOS应用开发的基础语言之一。Objective-C诞生于20世纪80年代,由Brace N. Koch等人设计并发展起来。随着iOS和macOS系统的流行,...
8. **Blocks与GCD**:介绍Objective-C中的Block语法和Grand Central Dispatch(GCD),用于并行处理和异步编程。 9. **Category与Extension**:解释如何使用Category扩展已有的类,以及Class Extension的概念和用途...
Objective-C是在C语言基础上扩展的,它引入了消息传递机制和类的概念,使得C语言具备了面向对象的能力。Objective-C的语法在某些方面与C++相似,但它的动态性更强,允许在运行时修改类和方法。 2. **类与对象**: ...
Objective-C是由C语言扩展而来的,增加了面向对象的特性,使得它更适合构建复杂的、可维护的软件系统。 在学习Objective-C时,首先需要理解其基础语法,包括类(Class)、对象(Object)、消息传递(Message ...