• block简介


    ios4.0系统已开始支持block,在编程过程中,blocks被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行。Blocks可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:blocks是inline的,并且它对局部变量是只读的。

    下面是理论部分:

      1、block的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    1 // 声明和实现写在一起,就像变量的声明实现 int a = 10;
     2        int (^aBlock)(int, int) = ^(int num1, int num2) {
     3 
     4        return num1 * num2;
     5 
     6      };
     7 // 声明和实现分开,就像变量先声明后实现 int a;a = 10;
     8         int (^cBlock)(int,int);
     9         cBlock = ^(int num1,int num2)
    10         {
    11             return num1 * num2;
    12         };

     

     其中,定义了一个名字为aBlock的blocks对象,并携带了相关信息:

      1、aBlock 有两个形式参数,分别为int类型;

      2、aBlock 的返回值为int 类型;

      3、等式右边就是blocks的具体实现;

      4、^ 带边blocks声明和实现的标示(关键字);

    当然,你可以定义其他形式的block。e.g:无返回值,无形式参数等;

    1
    2
    3
    4
    5
    1         void (^bBlock)() = ^()
    2         {
    3             int a = 10;
    4             printf(num = %d,a);
    5         };

      2、blocks 访问权限

      blocks可以访问局部变量,但是不能修改。

    1
    2
    3
    4
    5
    6
    1         int a = 10;
    2         int (^dBlock)(int) = ^(int num)
    3         {
    4             a++;//not work!
    5             return num * a;
    6         };

      此处不能修改的原因是在编译期间确定的,编译器编译的时候把a的值复制到block作为一个新变量(假设是a‘ = 10),此时a'和a是没有关系的。

    这个地方就是函数中的值传递。如果要修改就要加关键字:__block或者static

    1
    2
    3
    4
    5
    6
    1         __block int a = 7;
    2         int (^dBlock)(int) = ^(int num)
    3         {
    4             a++;// work!
    5             return num * a;
    6         };

      3、block的调用

      block调用就像调用函数一样。e.g:

    1 int c = aBlock(10,10);
      bBlock();

      4、block 应用

      假设我们熟悉代理递值的话,对代理我们可能又爱有恨!我们先建立模型A页面 push B页面,如果把A页面的值传递到B页面,属性和单例传值可以搞定!但是如果Pop过程中把B页面的值传递到A页面,那就可以用单例或者代理了!说到代理,我们要先声明协议,创建代理,很是麻烦。常常我们传递一个数值需要在两个页面间写很多代码,这些代码改变页面的整体顺序,可读性也打了折扣。所以,此时,block是一种优化方案!

    5、 block的内存管理

    block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。比如下面的例子。 我在view did load中创建了一个block:

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

    并且在一个按钮的事件中调用了这个block:

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

    此时我按了按钮之后就会导致程序崩溃,解决这个问题的方法就是在创建完block的时候需要调用copy的方法。copy会把block从栈上移动到堆上,那么就可以在其他地方使用这个block了~ 修改代码如下:

    1. _block = ^(){
    2. NSLog(@number %d, number);
    3. };
    4.  
    5. _block = [_blockcopy];

    同理,特别需要注意的地方就是在把block放到集合类当中去的时候,如果直接把生成的block放入到集合类中,是无法在其他地方使用block,必须要对block进行copy。不过代码看上去相对奇怪一些:

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


    6、循环引用
    对于非ARC下, 为了防止循环引用, 我们使用__block来修饰在Block中使用的对象:
    对于ARC下, 为了防止循环引用, 我们使用__weak来修饰在Block中使用的对象。原理就是:ARC中,Block中如果引用了__strong修饰符的自动变量,则相当于Block对该变量的引用计数+1。
    这一点其实是在第一点的一个小的衍生。当在block内部使用成员变量的时候,比如

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

    在block创建中:

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

    这里的_string相当于是self->_string;那么block是会对内部的对象进行一次retain。也就是说,self会被retain一次。当self释放的时候,需要block释放后才会对self进行释放,但是block的释放又需要等self的dealloc中才会释放。如此一来变形成了循环引用,导致内存泄露。
    修改方案是新建一个__block scope的局部变量,并把self赋值给它,而在block内部则使用这个局部变量来进行取值。因为__block标记的变量是不会被自动retain的。

    1
     
      1. __block ViewController *controller = self;
      2. _block = ^(){
      3. NSLog(@string %@, controller->_string);
      4. };
  • 相关阅读:
    用开源项目CropImage实现图片的裁剪(不推荐)
    设定当前视图中所有控件字体的方法
    用开源项目cropper实现对图片中任意部分进行裁剪
    从源码角度一步一步来修改PreferenceActivity界面
    自定义PreferenceActivity和PreferenceFragment的样式
    Eclipse 在线汉化的和修改字体大小、颜色的方法
    用level-list让同一个ImageView根据条件来显示不同的内容
    ClipDrawable属性介绍
    自己用图片做的可旋转、不确定进度的ProgressBar
    Android工具类 DateUtil,可以用它方便的进行日期的操作
  • 原文地址:https://www.cnblogs.com/allencelee/p/5008362.html
Copyright © 2020-2023  润新知