Block的那些事

Block 最大的便利就是简化的回调过程,实现编程之美。 使用Block要谨记别造成对象互相引用导致对象无法被释放。  

关于闭包:

http://baike.baidu.com/view/648413.htm

          闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。在 Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby 和 Python,objective c 等语言中都能找到对闭包不同程度的支持。

objective c 中的的闭包,是通过block实现的。Apple在C,Objective-C和C++中扩充了Block这种文法的,并且在GCC4.2中进行了支持。你可以把它理解为函数指针,匿名函数,闭包,lambda表达式,这里暂且用块对象来表述,因为它们之间还是有些许不同的。

如果以内联方式使用块对象,则无需声明。块对象声明语法与函数指针声明语法相似,但是块对象应使用脱字符(^)而非星号指针 (*);

 

关于Block官方文档:

http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html

Block objects are a C-level syntactic and runtime feature. They are similar to standard C functions, but in addition to executable code they may also contain variable bindings to automatic (stack) or managed (heap) memory. A block can therefore maintain a set of state (data) that it can use to impact behavior when executed.

You can use blocks to compose function expressions that can be passed to API, optionally stored, and used by multiple threads. Blocks are particularly useful as a callback because the block carries both the code to be executed on callback and the data needed during that execution.

Block objects are a C-level syntactic and runtime feature that allow you to compose function expressions that can be passed as arguments, optionally stored, and used by multiple threads. The function expression can reference and can preserve access to local variables. In other languages and environments, a block object is sometimes also called a closure or a lambda. You use a block when you want to create units of work (that is, code segments) that can be passed around as though they are values. Blocks offer more flexible programming and more power. You might use them, for example, to write callbacks or to perform an operation on all the items in a collection.

 

关于Block的内部实现:

http://blog.csdn.net/jasonblog/article/details/8077340

或原文地址:http://coolshell.cn/articles/8309.html

使用Clang命令查看中间代码:

clang src.m -rewrite-objc -o dis.cpp

下面Objective-C代码中的3个block,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

#import <Foundation/Foundation.h>

 

int(^maxBlk)(int,int) = ^(intm,intn){returnm > n ? m : n; };

 

intmain(intargc,constchar* argv[])

{

    ^{printf("Hello, World!\n"); } ();

 

    inti = 1024;

    void(^blk)(void) = ^{printf("%d\n", i); };

    blk();

 

    return0;

}

会产生对应的3个函数:

1

2

3

__maxBlk_block_func_0

__main_block_func_0

__main_block_func_1

可见函数的命名规则为:__{$Scope}_block_func_{$index}。其中{$Scope}为block所在函数,如果{$Scope}为全局就取block本身的名称;{$index}表示该block在{$Scope}作用域内出现的顺序(第几个block)。

Block结构体的实现:

以下代码为例:

  1. int i = 1024;  
  2. void(^blk)(void) = ^{ printf("%d\n", i); };  
  3. blk();  

BLOCK的实现

  1. #ifndef BLOCK_IMPL  
  2. #define BLOCK_IMPL  
  3. struct__block_impl {  
  4.     void *isa;  //结构体的类型
  5.     int Flags;  //标志位
  6.     int Reserved;  //保留位
  7.     void *FuncPtr; //见__main_block_func_0
  8. };  
  9. // 省略部分代码  
  10.    
  11. #endif  

__main_block_func_0的结构:

  1. static void __main_block_func_0(struct __main_block_impl_0 *__cself) {  
  2.     inti = __cself->i; // bound by copy  
  3.     printf("%d\n", i);  
  4. }  

 

__main_block_impl_0类似为self或this。结构为:

  1. struct __main_block_impl_0 {  
  2.     struct __block_impl impl;  
  3.     struct __main_block_desc_0* Desc;  
  4.     int i;  
  5.     __main_block_impl_0(void*fp, struct__main_block_desc_0 *desc, int_i, intflags=0) : i(_i) {  //i是值传递
  6.         impl.isa = &_NSConcreteStackBlock;  
  7.         impl.Flags = flags;  
  8.         impl.FuncPtr = fp;  
  9.         Desc = desc;  
  10.     }  
  11. }; 

__main_block_desc_0结构为:(从实现可看出为__main_block_impl_0的大小)

  1. static struct __main_block_desc_0 {  
  2.     unsigned long reserved;  
  3.     unsigned long Block_size;  
  4. } __main_block_desc_0_DATA = { 0, sizeof(struct__main_block_impl_0)};  

 所以

  1. int main(int argc, const char * argv[])  
  2. {  
  3.     int i = 1024;  
  4.     void(*blk)(void) = (void(*)(void))&__main_block_impl_0
  5. ((void*)__main_block_func_0, &__main_block_desc_0_DATA, i);  
  6.     ((void(*)(struct__block_impl *))((struct__block_impl *)blk)->FuncPtr)((struct__block_impl *)blk);  
  7.    
  8.     return 0;  
  9. }  

结论:

声明时,block本身,即__main_block_impl_0把数据保存在其结构体中,

执行时,调用block的对应函数,即FuncPtr,传递的参数是其自身__cself,即__main_block_impl_0

 

关于__block声明的实现:

声明__block对应会生成对应生成结构体:

  1. struct __Block_byref_i_0 {  
  2.     void *__isa;  
  3.     __Block_byref_i_0 *__forwarding;  
  4.     int __flags;  
  5.     int __size;  
  6.     int i;  
  7. };  

 

则block块声明实现时,变为:

  1. struct __main_block_impl_0 {  
  2.     struct __block_impl impl;  
  3.     struct __main_block_desc_0* Desc;  
  4.     __Block_byref_i_0 *i; // by ref  
  5.     __main_block_impl_0(void*fp, struct__main_block_desc_0 *desc, __Block_byref_i_0 *_i, intflags=0) : i(_i->__forwarding) {  
  6. //i为__Block_byref_i_0的结构体引用
  7.         impl.isa = &_NSConcreteStackBlock;  
  8.         impl.Flags = flags;  
  9.         impl.FuncPtr = fp;  
  10.         Desc = desc;  
  11.     }  
  12. };  

调用FuncPtr函数时,执行内容为:

  1. static void __main_block_func_0(struct __main_block_impl_0 *__cself) {  
  2.     __Block_byref_i_0 *i = __cself->i; // bound by ref  
  3.     (i->__forwarding->i) = 0; // 这个用法没能解释清楚  
  4.     printf("%d\n", (i->__forwarding->i));  
  5. }  

__block的本质是:

使用__block类型指示符的本质就是引入了__Block_byref_{$var_name}_{$index}结构体,而被__block关键字修饰的变量就被放到这个结构体中。另外,block结构体通过引入__Block_byref_{$var_name}_{$index}指针类型的成员,得以间接访问到外部变量。

(注:其实就是为了从值传递修改为引用,从而有了修改内容的能力)

 

关于block的copy。

When a block is copied, it creates strong references to object variables used within the block. If you use a block within the implementation of a method:

  • If you access an instance variable by reference, a strong reference is made to self;
  • If you access an instance variable by value, a strong reference is made to the variable.

  • dispatch_async(queue, ^{ 
    // instanceVariable is used by reference, self is retained 
    doSomethingWithObject(instanceVariable); 
    });
    id localVariable = instanceVariable; 
    dispatch_async(queue, ^{ 
    // localVariable is used by value, localVariable is retained (not self)
    doSomethingWithObject(localVariable); 
    });

总结:通过一系列demo测试验证,Block块的声明,只是初始化其结构体,保留函数指针和block块中变量,这个过程不会对block块中变量进行strong references。当调用block时,通过结构体中的函数指针和保留的变量来实现方法调用。当block出了声明区域时,系统会释放结构体。当block需要delay执行时,如dispatch_async方法,需将block进行copy,这时会对block内变量进行strong references,且block结构体会copy到堆上。而实时调用block,如dispatch_sync方法或enumerateObjectsUsingBlock方法等方法,则直接调用保留的变量。当然,GCD方法在多线程上会数据线程安全做相应处理,但也不需要我们care。而类似NSArray的block相对很单纯,使用时不需要过多考虑。

 

======================神奇的分隔线线======================

择一的内容补充:http://www.cnblogs.com/biosli/archive/2013/05/29/iOS_Objective-C_Block.html#commentform

在经过copy之后,对象的类型从__NSStackBlock__变为了__NSMallocBlock__

在Objective-C的设计中,我没见过copy一回把对象的类型也给变了的,再次说明了block是一种特殊的对象。

大家应该注意到__block标记的变量了吧,这个变量会随着block对象上堆一块上堆,这个部分上面的blogs和书中都有讲解,我就不叙述了。

另外还有一种类型block的类型__NSGlobalBlock__,当block里面没有局部变量的时候会block会变为这个类型,这个类型的retain/copy/release全都不会对对象有影响,可以当做静态block理解。

__NSMallocBlock__对象再次copy,不会再产生新的对象而是对原有对象进行retain。

经过实验几个block类型的retain/copy/release的功能如下(非ARC环境):

 

说说ARC
上面的这些方法,说的都是非ARC编程的时候的注意事项,在ARC下很多规则都可以省略了。
因为在ARC下有个原则,只要block在strong指针底下过一道都会放到堆上。
看下面这个实验: 

    {

        __blockint val = 10;

        __strong blk strongPointerBlock = ^{NSLog(@"val = %d", ++val);};

        NSLog(@"strongPointerBlock: %@", strongPointerBlock); //1        

        __weak blk weakPointerBlock = ^{NSLog(@"val = %d", ++val);};

        NSLog(@"weakPointerBlock: %@", weakPointerBlock); //2        

        NSLog(@"mallocBlock: %@", [weakPointerBlock copy]); //3        

        NSLog(@"test %@", ^{NSLog(@"val = %d", ++val);}); //4

    }

得到的日志:

2013-05-29 16:03:58.773 BlockTest[3482:c07] strongPointerBlock: <__NSMallocBlock__: 0x7625120>

2013-05-29 16:03:58.776 BlockTest[3482:c07] weakPointerBlock: <__NSStackBlock__: 0xbfffdb30>

2013-05-29 16:03:58.776 BlockTest[3482:c07] mallocBlock: <__NSMallocBlock__: 0x714ce60>

2013-05-29 16:03:58.777 BlockTest[3482:c07] test <__NSStackBlock__: 0xbfffdb18>

 

================ 华丽的分隔线 ================

在垃圾回收机制里面,如果你同时使用__weak 和__block 来标识一个变量,那么该 block 将不会保证它是一直是有效的。

一个 block 的文本(通常是^{...})是一个代表 block 的本地栈数据结构地址。 因此该本地栈数据结构的作用范围是封闭的复合状态,所以你应该避免下面例子显示的模式: 

void dontDoThis() { 
void (^blockArray[3])(void); // an array of 3 block references
    for (int i = 0; i < 3; ++i) { 
        blockArray[i] = ^{ printf("hello, %d\n", i); }; 
        // WRONG: The block literal scope is the "for" loop 
    }
}
posted @ 2012-03-12 10:27  simalone  阅读(201)  评论(0编辑  收藏  举报