`
supclass
  • 浏览: 6754 次
  • 性别: Icon_minigender_1
最近访客 更多访客>>
社区版块
存档分类
最新评论

Objective-C Block

阅读更多
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)。
看个列子
^(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


再次提醒要记得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==

因为在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..zip

    在这个名为"The Objective-C block utilities you always wish you had"的压缩包中,很显然,它包含了一些用于增强Objective-C中Block功能的实用工具。Blocks是Objective-C中的一个强大特性,它们允许程序员定义可...

    Objective-C高级编程 iOS与OS X多线程和内存管理_Objective-C_ios_

    书中还可能涵盖Block(代码块)的使用,这是Objective-C中一种强大的语法特性,常用于多线程和异步编程,因为它可以捕获和封装变量的上下文。另外,可能会讲解到Cocoa Touch和Cocoa框架中的线程安全类和方法,以及...

    Objective-C2.0程序设计第二版中文版及代码

    1. **Objective-C基础**:Objective-C是在C语言基础上扩展的,添加了面向对象特性。它的基础包括类、对象、消息传递等概念。类定义了对象的属性和行为,对象则是类的实例。消息传递是Objective-C的核心,通过`...

    Objective-C

    Objective-C是在C语言的基础上扩展了Smalltalk式的面向对象特性,使得它既有C语言的强大功能,又具备了面向对象编程的灵活性。 在Objective-C中,类是所有对象的基础。类定义了一组属性(实例变量)和方法(函数)...

    Objective-C基础教程(Learn Objective-C)随书源码下载

    Objective-C是C语言的超集,它扩展了C语言,加入了面向对象的特性。面向对象编程(OOP)的核心概念包括类、对象、继承、封装和多态。Objective-C通过消息传递机制实现了这些概念,使得代码更加模块化和可维护。 2....

    objective-c编程 第2版=objective-c program 2nd edition_13889311

    7. **Block和GCD**:Block是Objective-C中的匿名函数,可以用于简化回调和并发处理。Grand Central Dispatch(GCD)是Apple提供的多线程解决方案,书中可能讲解如何利用它们优化程序性能。 8. **Cocoa Touch和App...

    Effective Objective-C 2.0

    1. Objective-C语言简介:Objective-C是一种通用的编程语言,其被广泛应用于苹果公司的macOS、iOS、watchOS和tvOS操作系统。它基于C语言,并加入了Smalltalk风格的消息传递机制。 2. 类和消息机制:Objective-C语言...

    Objective-C2.0程序设计习题官方答案

    Objective-C2.0是苹果公司开发的一种面向对象的编程语言,它是C语言的超集,融合了Smalltalk的动态特性,广泛应用于iOS和Mac OS X操作系统。本资料集中的"Objective-C2.0程序设计习题官方答案"显然是针对学习...

    从 C++ 到 Objective-C 快速精通

    C++11也引入了类似的概念,但Objective-C的Block在语法和使用上有所不同。 6. Foundation框架:Objective-C的基础库Foundation框架提供了许多核心的类和功能,如NSArray、NSDictionary等容器类,以及NSString、...

    Objective-C语言教程与案例.zip

    还可能讲解了Objective-C的特殊特性,如类别(Category)、协议(Protocol)和块(Block)。 2. **语法结构**:Objective-C的语法有其独特性,比如它的方法定义和调用方式,以及在C语言基础上添加的“@”符号,如@...

    [Objective-C编程(第6版)]Programming in Objective-C

    - **块(Block)**:块是一种匿名函数,在Objective-C中常用于回调函数或者异步操作的完成处理。 #### 应用场景 - **iOS应用开发**:Objective-C是开发iOS应用的主要语言之一,尤其适合于那些需要高性能和复杂用户...

    Objective-C高级编程†

    Block(块)是Objective-C的另一项重要特性,它是一种内联函数,可以捕获并存储其定义时的作用域内的变量。Block常用于异步操作的回调,如网络请求或GCD(Grand Central Dispatch)。 Foundation框架是Objective-C...

    Objective-C示例代码1

    10. **Block(闭包)**:Objective-C支持块语法,`FractionDemo`可能包含使用块作为参数或返回值的方法,用于实现异步操作或回调。 这些只是Objective-C编程中的一部分核心概念,实际的`FractionDemo`代码会根据...

    深入浅出讲objective-c

    1. **语法特性**:Objective-C是在C语言的基础上扩展的,保留了C的语法特性,同时引入了消息传递机制,使得它具有面向对象的能力。 2. **类和对象**:Objective-C中的所有数据结构都是基于类的,类是对象的模板,而...

    objective-c基础教程(附源代码)

    首先,Objective-C是在C语言的基础上扩展的,因此,理解C语言的基本语法是学习Objective-C的前提。它引入了消息传递机制,这是Objective-C的关键特性,允许对象之间进行通信。消息传递类似于函数调用,但更加灵活,...

    0基础iOS开发学习计划Objective-c语言内容概述.doc

    Objective-C是一种结合了C语言特性和面向对象编程思想的语言,它是苹果iOS和macOS应用开发的基础语言之一。Objective-C诞生于20世纪80年代,由Brace N. Koch等人设计并发展起来。随着iOS和macOS系统的流行,...

    Learn Objective-C On The Mac(英文PDF+SourceCode)

    8. **Blocks与GCD**:介绍Objective-C中的Block语法和Grand Central Dispatch(GCD),用于并行处理和异步编程。 9. **Category与Extension**:解释如何使用Category扩展已有的类,以及Class Extension的概念和用途...

    objective-c学习资料

    Objective-C是在C语言基础上扩展的,它引入了消息传递机制和类的概念,使得C语言具备了面向对象的能力。Objective-C的语法在某些方面与C++相似,但它的动态性更强,允许在运行时修改类和方法。 2. **类与对象**: ...

    iphone(Objective-C)

    Objective-C是由C语言扩展而来的,增加了面向对象的特性,使得它更适合构建复杂的、可维护的软件系统。 在学习Objective-C时,首先需要理解其基础语法,包括类(Class)、对象(Object)、消息传递(Message ...

Global site tag (gtag.js) - Google Analytics