zoukankan      html  css  js  c++  java
  • OC-精简解读 block

      网上关于block的解释已经很多了,在此,本文仅对block的精简解读。

    一、block是什么?

      block在OC中是对象,在运行时会被转化成函数。但是block与OC中其他对象最大的不同是:一般来说,block初始化是储存在栈区的。关于block在内存中的储存位置我们在下面的内容中探讨。因为情况是在是太多了。

      block的定义:

    struct Block_descriptor {
        unsigned long int reserved;
        unsigned long int size;
        void (*copy)(void *dst, void *src);
        void (*dispose)(void *);
    };
    
    
    struct Block_layout {
        void *isa;  //isa指针,这是OC对象的证明
        int flags;
        int reserved; 
        void (*invoke)(void *, ...);  //这个invoke指针指向block被转化成的函数
        struct Block_descriptor *descriptor;
        /* Imported variables. */  //如果引用了外部变量,从这里开始列在后面
    };
    

    二、block内存地址研究

      在开始研究之前,需要说明的是,block中可以访问并且修改全局变量和static变量(无论全局还是局部的)。如果访问非static局部变量,block会被转换成函数,那么就是值传递,并且不能在block中修改传进来的值,编译器会报错的。如果一定要修改非static局部变量的值,请加__block,这样的话,就是传递变量的指针,当然可以修改。

      下面简单研究下block的内存地址。

    int a=1;
    static int b=2;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        int c=3;
        static int d=4;
        
        void(^myBlock)()=^{
            NSLog(@"myBlock");
        };
        NSLog(@"myBlock   %p",myBlock);//没有访问任何外部变量,block储存在全局区(常量区)
        
        NSArray * blockArr=[NSArray arrayWithObjects:^{a=a+c;},        //访问全局变量和局部变量,block储存在堆区
                                           ^{int ss= b +1;}, //访问了全局静态变量,block储存在全局区(常量区)
                                  ^{int dd=c+1;}, //访问了局部变量,block储存在栈区
                                  ^{d=5+c;},nil];  //访问了局部静态变量和局部变量,block储存在栈区 NSLog(@"%@",blockArr);

     打印结果如下:

    2015-11-03 21:21:40.936 NSRunloopTest[3668:302834] myBlock   0x10c350310
    2015-11-03 21:21:40.937 NSRunloopTest[3668:302834] (
        "<__NSMallocBlock__: 0x7feef1732480>",
        "<__NSGlobalBlock__: 0x10c350370>",
        "<__NSStackBlock__: 0x7fff538b3a08>",
        "<__NSStackBlock__: 0x7fff538b39e0>"
    ) 

     解释: 为了打印出三种不同类型的block,就随便组合了几种外部变量访问的方式,先说类型,

    __NSMallocBlock__:内存地址在堆区的block类型

    __NSGlobalBlock__:内存地址在全局区的block类型

    __NSStackBlock__: 内存地址在栈区的block类型

    比较对应的内存地址,也确实如此,当然,英文名称也说明了一切。block是否访问外部变量,外部变量的类型不同,同时访问多种变量,ARC和MRC,种种条件组合起来,情况是不一样的,下面是一些常见组合的结论:

    在Block中, 如果只使用全局或静态变量或不使用外部变量, 那么Block块的代码会存储在全局区;
    如果使用了外部变量, 在ARC中, Block块的代码会存储在堆区;
    在MRC中, Block快的代码会存储在栈区;
    block默认情况下不能修改外部变量, 只能读取外部变量:
    在ARC中, 外部变量存在堆中, 这个变量在Block块内与在Block块外地址相同;
    外部变量存在栈中, 这个变量会被copy到为Block代码块所分配的堆中;
    在MRC中, 外部变量存在堆中, 这个变量在Block块内与Block块外相同;
    外部变量存在栈中, 这个变量会被copy到为Block代码块所分配的栈中;
    如果需要修改外部变量, 需要在外部变量前面声明 __block
    在ARC中, 外部变量存在堆中, 这个变量在Block块内与Block块外地址相同;
    外部变量存在栈中, 这个变量会被转移到堆区, 不是复制, 是转移.
    在MRC中, 外部变量存在堆中, 这个变量在Block块内与Block块外地址相同;
    外部变量存在栈中, 这个变量在Block块内与Block块外地址相同; 
     
    好吧,其他情况就待君探索了。
     
    三、声明block属性的时候,为什么用copy
      一般而言,block初始化时是储存在栈区的(感觉这么说没有底气,你懂的。。。),此时的block和局部变量一样,代码块结束,就被释放掉了,不用copy的话,就是野指针;用copy拷贝block到堆区来,这样可以长久的调用对应的block,block成为属性值才有意义。
      是不是有人在心里这么问了:为什么NSString声明成属性的时候也用copy ?
      因为copy做了这么两件事:
      1.对原来的NSString对象做了一次retain
      2.返回原来NSString对象的指针
      并且,copy过来是只读的,不能改,我猜官方是为了某种意义上的统一吧。
     
    如果有不足之处,请指出,多多交流学习。在此谢过!
     

      

  • 相关阅读:
    使用VC++生成调试信息
    在Xp home上安装Rose 2003
    SkyDrive注册方法
    vsftpd同时使用系统用户和虚拟用户验证
    如何查看linux系统版本
    在RedHat AS中安装SVN
    Vnc & Gdm
    (转)如何:在设备上安装 SQL Server Compact 3.5
    java培训学习笔记一
    因为此版本的应用程序不支持其项目类型(.csproj),若要打开它,请使用支持此类型项
  • 原文地址:https://www.cnblogs.com/panyuluoye/p/4934555.html
Copyright © 2011-2022 走看看