Block 分析

摘录:http://www.cnblogs.com/goahead-yingjun/articles/4478425.html
参考:http://www.cocoachina.com/bbs/read.php?tid=152222

前言

Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事。需要注意的是由于Objective-C在iOS中不支持GC机制,使用Block必须自己管理内存,而内存管理正是使用Block坑最多的地方,错误的内存管理 要么导致return cycle内存泄漏要么内存被提前释放导致crash。 Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,Block不仅 实现函数的功能,还能携带函数的执行环境。

可以这样理解,Block其实包含两个部分内容

Block执行的代码,这是在编译的时候已经生成好的;
一个包含Block执行时需要的所有外部变量值的数据结构。 Block将使用到的、作用域附近到的变量的值建立一份快照拷贝到栈上。

Block与函数另一个不同是,Block类似ObjC的对象,可以使用自动释放池管理内存(但Block并不完全等同于ObjC对象)

Block的类型与内存管理

根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。

NSGlobalBlock:类似函数,位于text段(不在堆栈中);
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存。

1、NSGlobalBlock

block在自己的环路(代码块)中不会抓取任何外部变量,它不需要在在运行的引用其他外包变量,便不会在运行的时候将block压入栈中,而会作为一个NSGlobalBlock编译。它既不是栈也不是堆,而是代码片段的一部分。所以它不管在什么环境下(MRC、ARC)都能正常运行。

如下,我们可以通过是否引用外部变量识别,未引用外部变量即为NSGlobalBlock。

void exampleA() {
    //create a NSGlobalBlock
    float (^sum)(float, float) = ^(float a, float b){
        return a + b;
    };

    NSLog(@"block is %@", sum); //block is <__NSGlobalBlock__: 0x47d0>

}

2.NSStackBlock和NSMallocBlock

如果block有引用外部的局部变量,那么该block便会被压入调用它的函数的栈中,编译器会将其编译为NSStackBlock类型。当其调用它的函数结束时,栈会被清空,同时在栈中的block也会被清除,如果此时有其他函数调用此block便会野指针报错。要解决这个问题,需要手动对block进行copy,通过copy后,并设为自动释放,会将copy分配到堆中,并交由最近释放池负责释放,如:

[[stackBlock copy] autorelease]

但是,在ARC环境下,block会默认分配到堆中,作为一个自动释放的NSMallocBlock(即不受栈被清空的影响)。

代码如下:

typedef void (^dBlock)();
dBlock exampleB_getBlock(){
    char b = 'B';
    void (^block)() = ^{
        printf("%c\n", b);
    };
    NSLog(@"block:%@",block);

    dBlock block_copy=[[block copy] autorelease];
    NSLog(@"block_copy:%@",block_copy);

//    return block_copy;
    return block;
}

void exampleB() {
    dBlock block=exampleB_getBlock();
    block();
}

在MRC环境中,运行后,打印为

block:<__NSStackBlock__: 0xbffbff68>
block_copy:<__NSMallocBlock__: 0x78e66430>

说明block经过copy后,被分配到了堆上。
当返回block时,因为是NSStackBlock,所以block();野指针报错。
当返回block_copy时,因为是NSMallocBlock,运行正常。

如果在ARC环境下,block不经过copy,便默认是NSMallocBlock,直接返回block就可以了。

PS

NSMallocBlock只需要对NSStackBlock进行copy操作就可以获取,但是retain操作就不行,会在下面说明
Block的copy、retain、release操作

不同于NSObjec的copy、retain、release操作:

  • Block_copy与copy等效,Block_release与release等效;
  • 对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
  • NSGlobalBlock:retain、copy、release操作都无效;
  • NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry
    addObject:stackBlock],(补:在arc中不用担心此问题,因为arc中会默认将实例化的block拷贝到堆上)在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock
    copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy]
    autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
  • NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
  • 尽量不要对Block使用retain操作。

Block对外部变量的存取管理

如果block对外部变量做了引用,那他们之间会有什么影响呢?

基本数据类型 的引用

1、局部变量

void exampleC() {
    char b = 'B';
    NSLog(@"%p,%c",&b,b);

    void (^block)() = ^{
        NSLog(@"%c",b);
    };

    block();//修改前

    b='C';
    NSLog(@"%p,%c",&b,b);

    block();//修改后
}

打印:

0xbffa8f8b,B
0xbffa8f6c,B
0xbffa8f8b,C
0xbffa8f6c,B

执行后block两次打印结果为两个B,说明,对于基本数据类型的局部变量,当block对其引用的时候,会创建下该变量的一份快照(新建一个同类型的变量,并保存相同值),变量后面值的改变不会影响快照的值,在创建block的时候本质上已经分为两个独立的变量了。
注意:局部变量(无__block修饰)的快照,是只读的,不能在block中修改其值,包括指针变量也不能修改其引用地址。

2.静态变量 全局变量

void exampleC() {
    static int num=1;
    NSLog(@"%p,%d",&num,num);
    void (^block)() = ^{
       NSLog(@"%p,%d",&num,num);
    };

    block();//修改前

    num=2;
    NSLog(@"%p,%d",&num,num);

    block();//修改后
}

执行后打印了:

0x647f0,1
0x647f0,1
0x647f0,2
0x647f0,2

变量地址都是一样,说明没有创建快照,而是直接引用,因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,所以获取到的是最新值。

3、__block修饰的变量
被__block修饰的变量称作Block变量。 Block变量等效于全局变量、或静态变量。当block位于栈区时,block变量使用的都是相同变量,而当block位于堆区时,block变量一样会创建快照,使用快照代替原变量,具体看下面会讲到。

OC对象 的引用

iOS中,oc对象本质上是一个指向结构体的指针变量。 block对oc对象的引用,其本质是对其指针地址的引用,因此,block引用时,实际上是对指针变量做了一份快照(新创建一个指针变量,指向相同的地址)。所以,获取到的oc对象取决于快照(指针变量)指向的值,如果其指向的值发生变化,block引用到的值自然也跟着变了。下面看例子:

void exampleC() {
    NSString *str=[NSString stringWithFormat:@"%@",@"str"];
    NSLog(@"%p,%p,%@",&str,str,str);

    void (^block)() = ^{
        NSLog(@"%p,%p,%@",&str,str,str);
        NSLog(@"-----------------");
    };

    block();//修改前

    str=[NSString stringWithFormat:@"%@",@"newStr"];
    NSLog(@"%p,%p,%@",&str,str,str);

    block();//修改后
}

执行后,打印

0xbffd7f84,0x7b7942b0,str
0xbffd7f74,0x7b7942b0,str
-----------------
0xbffd7f84,0x7b896950,newStr
0xbffd7f74,0x7b7942b0,str
-----------------

从结果看,创建的指针变量str与在block内引用的str快照的变量地址是不同的0xbffd7f84和0xbffd7f74,说明,在block在引用该对象的时候,创建了一个相同类型的oc对象快照(指针变量),保存相同的值,因为是指针,所以值为引用地址0x7b7942b0。当修改str时,其指针指向新对象地址0x7b896950,但快照依然指向了0x7b7942b0,因此还是打印出了str字符串。

如果你硬要问,oc对象的处理方式不是用指针变量来理解,那我就验证一下给你看:

void exampleC() {
    char b = 'B';
    char *pb=&b;

    NSLog(@"%p,%p,%c",&pb,pb,*pb);

    void (^block)() = ^{
        NSLog(@"%p,%p,%c",&pb,pb,*pb);
        NSLog(@"-----------------");
    };

    block();//修改前

    *pb='C';
    NSLog(@"%p,%p,%c",&pb,pb,*pb);

    block();//修改后
}

打印结果:

0xbffe0f84,0xbffe0f8b,B
0xbffe0f6c,0xbffe0f8b,B
-----------------
0xbffe0f84,0xbffe0f8b,C
0xbffe0f6c,0xbffe0f8b,C
-----------------

验证以上oc对象引用的说法。block的快照就是变量类型相同的是两个变量,保存相同的值,如果是oc对象就引用相同对象(地址)。

依托上面的论证,oc对象变化的时候,如果改变引用对象的地址,block返回到的就一直是最新的值。这种情况在使用oc可变对象的时候会出现,如NSMutableArray、NSMutableString等等。代码如下:

void exampleC() {
    NSMutableArray *list=[NSMutableArray arrayWithArray:@[@"a",@"b",@"c"]];
//    NSMutableString *mutStr=[NSMutableString stringWithFormat:@"abc"];

    NSLog(@"%p,%p,%@",&list,list,list);

    void (^block)() = ^{
        NSLog(@"%p,%p,%@",&list,list,list);
        NSLog(@"-----------------");
    };

    block();//修改前

    //[mutStr appendString:@"123"];

    [list removeObjectAtIndex:0];
    NSLog(@"%p,%p,%@",&list,list,list);

    block();//修改后
}

执行后结果为

0xbffcff7c,0x7b063c10,(
    a,
    b,
    c
)
0xbffcff6c,0x7b063c10,(
    a,
    b,
    c
)
-----------------
0xbffcff7c,0x7b063c10,(
    b,
    c
)
0xbffcff6c,0x7b063c10,(
    b,
    c
)
-----------------

从打印结果可看出,虽然其快照与外部变量不同,但可变对象在修改后,其引用地址是不会改变,一直都是0x7b063c10,因此,block一直都能引用到最新的值。因为不会改变快照的引用地址,所以,是可以在block中修改可变oc对象的

Block 对 OC对象 的持有

block对于objc对象的内存管理较为复杂,当block被分配到堆区的时候,其对引用到的外部变量的持有也会发生变化,如果处理不好,就会造成循环引用等问题。 这里要分static静态变量、 global全局变量 、local局部变量、 block变量分析,还要区分非arc和arc环境的影响。

  1. MRC环境下
    代码如下:
@interface ViewController : UIViewController
{
    NSObject *instanceObj;
}
@end

NSString* __globalObj = nil;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self exampleD];
}

- (void)exampleD{

    static NSString *__staticObj;

    __globalObj = [[NSString alloc] initWithFormat:@"global"];

    instanceObj = [[NSString alloc] initWithFormat:@"instance"];

    __staticObj = [[NSString alloc] initWithFormat:@"static"];

    NSString* localObj = [[NSString alloc] initWithFormat:@"local"];

    __block NSString* blockObj =[[NSString alloc] initWithFormat:@"block"];

    NSLog(@"-----------原变量-------------");
    NSLog(@"%p,%p,%@",&__globalObj,__globalObj, __globalObj);

    NSLog(@"%p,%p,%@",&__staticObj,__staticObj, __staticObj);

    NSLog(@"%p,%p,%@",&instanceObj,instanceObj, instanceObj);

    NSLog(@"%p,%p,%@",&localObj,localObj, localObj);

    NSLog(@"%p,%p,%@",&blockObj,blockObj, blockObj);
    NSLog(@"---------------------------------");

    NSLog(@"self:%d",[self retainCount]);

    dBlock aBlock = ^{

        NSLog(@"%p,%p,%@",&__globalObj,__globalObj, __globalObj);

        NSLog(@"%p,%p,%@",&__staticObj,__staticObj, __staticObj);

        NSLog(@"%p,%p,%@",&instanceObj,instanceObj, instanceObj);

        NSLog(@"%p,%p,%@",&localObj,localObj, localObj);

        NSLog(@"%p,%p,%@",&blockObj,blockObj, blockObj);

    };

    NSLog(@"----------被block引用后的变量---------");
    aBlock();
    NSLog(@"--------------------------------");

    NSLog(@"self:%d",[self retainCount]);

    aBlock = [[aBlock copy] autorelease];

    NSLog(@"self:%d",[self retainCount]);

    NSLog(@"-------block被分配堆区后引用的变量---------");
    aBlock();
    NSLog(@"--------------------------------");

    NSLog(@"\n-------block被分配堆区后引用的变量计数-------------");
    NSLog(@"globalObj:%d", [__globalObj retainCount]);

    NSLog(@"staticObj:%d", [__staticObj retainCount]);

    NSLog(@"instanceObj:%d", [instanceObj retainCount]);

    NSLog(@"localObj:%d", [localObj retainCount]);

    NSLog(@"blockObj:%d", [blockObj retainCount]);


}
@end

运行后,打印

-----------原变量-------------
0x2f9d4,0x79066450,global
0x2f9d8,0x79062eb0,static
0x790e256c,0x79062e90,instance
0xbffd2f94,0x790628f0,local
0xbffd2f90,0x790e3d20,block
---------------------------------
self:25
----------block引用后的变量---------
0x2f9d4,0x79066450,global
0x2f9d8,0x79062eb0,static
0x790e256c,0x79062e90,instance
0xbffd2f60,0x790628f0,local
0xbffd2f90,0x790e3d20,block
--------------------------------
self:25
self:26
-------block被分配堆区后引用的变量---------
2016-04-20 16:06:23.827 BlockTest[3567:234612] 0x2f9d4,0x79066450,global
2016-04-20 16:06:23.827 BlockTest[3567:234612] 0x2f9d8,0x79062eb0,static
2016-04-20 16:06:23.827 BlockTest[3567:234612] 0x790e256c,0x79062e90,instance
2016-04-20 16:06:23.827 BlockTest[3567:234612] 0x78f692e8,0x790628f0,local
2016-04-20 16:06:23.827 BlockTest[3567:234612] 0x78f68ab8,0x790e3d20,block
--------------------------------

-------block被分配堆区后引用的变量计数-------------
globalObj:1
staticObj:1
instanceObj:1
localObj:2
blockObj:1

从结果分析可看出,

  • globalObj和staticObj在内存中的位置是确定的,所以Block
    copy时不会retain对象。而blockObj的作用类型跟globalObj和staticObj相似,也不会retain。
  • localObj在Block copy时,系统自动retain对象,增加其引用计数,所以局部变量,记得release一次。
  • _instanceObj在Block copy时也没有直接retain _instanceObj对象本身,但会retain self。所以在Block中可以直接读写_instanceObj变量。

从内存上讲,在block copy的时候,是重新在堆区开辟一块内存空间,将block保存进去,将NSStackBlock变为NSMallocBlock。
我们都知道,在block引用外包oc的时候,会创建一份oc变量(指针变量)快照,其中只对localObj创建快照,而其他类型是直接使用同一个变量。
为了保证堆区block中的快照(指针变量)指针引用到的localObj和instanceObj有效,避免因栈区清空导致localObj和instanceObj被销毁造成野指针,因此,编译器在copy block的时候,会对其引用到的localObj和instanceObj的self作一次retain。而globalObj、staticObj和blockObj则不存在这种问题。

PS:
blockObj与globalObj、staticObj不同的是,在block未被copy到堆区的时候,在block里blockObj是直接使用的,但是当block被copy时,blockObj其实也会另外做一份快照,blockObj在block copy前后,blockObj变量地址是不同的,只是引用地址相同而已,但是跟局部变量的快照不同,它可以修改其值(引用地址)的。所以其作用是跟globalObj、staticObj一样的。

  1. ARC环境下

在arc环境下,引用外部变量的block会被自动分配到堆区(相当于默认帮我们做了copy)。由于arc中没有retain,retainCount的概念。只有强引用和弱引用的概念。当一个变量没有__strong的指针指向它时,就会被系统释放。因此我们可以通过为变量设置一个week弱引用来检测引用对象,一旦引用对象被释放,week就会为nil, 下面代码来测试:

__weak NSString *globalObj_week;
__weak NSString *instanceObj_week;
__weak NSString *staticObj_week;
__weak NSString *localObj_week;
__weak NSString *blockObj_week;

- (void)exampleD{

    static NSString *__staticObj;

    __globalObj = [[NSString alloc] initWithFormat:@"global"];

    instanceObj = [[NSString alloc] initWithFormat:@"instance"];

    __staticObj = [[NSString alloc] initWithFormat:@"static"];

    NSString* localObj = [[NSString alloc] initWithFormat:@"local"];

    __block NSString* blockObj =[[NSString alloc] initWithFormat:@"block"];

    NSLog(@"-----------原变量-------------");
    NSLog(@"%p,%p,%@",&__globalObj,__globalObj, __globalObj);

    NSLog(@"%p,%p,%@",&__staticObj,__staticObj, __staticObj);

    NSLog(@"%p,%p,%@",&instanceObj,instanceObj, instanceObj);

    NSLog(@"%p,%p,%@",&localObj,localObj, localObj);

    NSLog(@"%p,%p,%@",&blockObj,blockObj, blockObj);
    NSLog(@"---------------------------------");



    globalObj_week=__globalObj;
    instanceObj_week=instanceObj;
    staticObj_week=__staticObj;
    localObj_week=localObj;
    blockObj_week=blockObj;

    dBlock aBlock = ^{

        NSLog(@"%p,%p,%@",&__globalObj,__globalObj, __globalObj);

        NSLog(@"%p,%p,%@",&__staticObj,__staticObj, __staticObj);

        NSLog(@"%p,%p,%@",&instanceObj,instanceObj, instanceObj);

        NSLog(@"%p,%p,%@",&localObj,localObj, localObj);

        NSLog(@"%p,%p,%@",&blockObj,blockObj, blockObj);


        NSLog(@"--------------week--------------");
        NSLog(@"globalObj_week:%@",globalObj_week);
        NSLog(@"instanceObj_week:%@",instanceObj_week);
        NSLog(@"staticObj_week:%@",staticObj_week);
        NSLog(@"localObj_week:%@",localObj_week);
        NSLog(@"blockObj_week:%@",blockObj_week);

    };

    __globalObj=nil;
    instanceObj=nil;
    __staticObj=nil;
    localObj=nil;
    blockObj=nil;

    NSLog(@"-------block被分配堆区后引用的变量---------");
    aBlock();
    NSLog(@"--------------------------------");

}

执行结果:

-----------原变量-------------
0xee9d8,0x7d06f2d0,global
0xee9f0,0x7d06e680,static
0x7be7221c,0x7d0706e0,instance
0xbff13f90,0x7d06e690,local
0xbff13f88,0x7d06f410,block
---------------------------------
-------block被分配堆区后引用的变量---------
0xee9d8,0x0,(null)
0xee9f0,0x0,(null)
0x7be7221c,0x0,(null)
0x7be74ed8,0x7d06e690,local
0x7be74ef8,0x0,(null)
--------------week--------------
globalObj_week:(null)
instanceObj_week:(null)
staticObj_week:(null)
localObj_week:local
blockObj_week:(null)
--------------------------------

从结果可看出,最后只有localObj未被释放掉,说明在block被分配堆区的时候,局部变量的快照变量会对引用对象再做一次强引用。
成员变量instanceObj不做快照不再做强引用,是因为block已经对self做了一次强引用(与MRC同理),这会引发另外一个问题“循环引用”。

PS:
在arc环境下的blockObj,在block分配到堆区的时候跟MRC环境一样会创建一个可修改的快照,但该快照会对blockObj引用的对象作强引用,原变量blockObj销毁(block分配到堆区后,你所使用的变量blockObj其实是新建的快照,其变量地址与原blockObj变量地址是不一样的),所以,在创建block后,blockObj=nil其实操作的是快照,结果就把引用对象销毁了blockObj_week:(null)

注意:
这里对oc对象赋值的时候可不能使用objec=@"objec";的方法赋值,因为@"objec"是常量(不在堆栈),常量是不遵循arc原则的,即创建后就一直存在,不管有多少强引用,它都不会被销毁。如下代码:

    NSString* couObj =@"1";
    __weak NSString *wobj=couObj;
    couObj=nil;
    NSLog(@"%@",wobj);

执行后还是会打印1

Block的循环引用

根据上面的结论, 因为block在拷贝到堆上的时候,会retain(强引用)其引用的外部变量,那么如果block中如果引用了他的宿主对象self(使用其成员变量或者显式使用了self),如果这时self的成员变量或者属性又对block做持有,那就会引起循环引用,如下:

//ARC:myblock为strong
//MRC:myblock为copy
self.myblock = ^{
        [self doSomething];
        //或者 NSLog(@"%p,%p,%@",&instanceObj,instanceObj, instanceObj);
  };

或者

//ARC
_myblock = ^{
        [self doSomething];
        //或者 NSLog(@"%p,%p,%@",&instanceObj,instanceObj, instanceObj);
  };
  1. 在MRC环境下

如果self的成员变量或者copy属性对block做了持有,如

@property (nonatomic,copy) dBlock myBlock;

dBlock aBlock = ^{

        [self doSomething];

        NSLog(@"%p,%p,%@",&instanceObj,instanceObj, instanceObj);

    };

    self.myBlock=aBlock;
    //或者_myBlock=[aBlock copy];

这里,copy属性的myBlock 对block做了持有。说到这,要搞清楚,只有在堆区的block即NSMallocBlock才会对里面的外部对象做持有,对外部对象做持有才有可能出现循环引用的问题,如果这里block是没经过copy的,只是NSStackBlock,NSStackBlock会因栈清空而释放掉,则不存在循环引用问题。所以,这里讨论的block是NSMallocBlock。
这里self的myBlock属性对在堆区的aBlock做了持有,而aBlock中有引用了self或者成员变量,所以aBlock又对self做了持有,造成循环。
要解决这个问题就必须打破循环,我们可以从aBlock中对self或者成员变量的引用入手,让aBlock间接使用self,而不对其做retain,代码做如下修改:

__block typeof(instanceObj) week_instanceObj=instanceObj;
__block typeof(self) weekself=self;

    dBlock aBlock = ^{

        [weekself doSomething];

       NSLog(@"%p,%p,%@",&week_instanceObj,week_instanceObj, week_instanceObj);


    };

使用__block来间接引用,上面“Block 对 OC对象 的持有”MRC环境分析提过,__block变量在block copy中,使用的是week_instanceObj的快照,该快照引用对象与instanceObj一样,但不做retain,不改变引用计数。

  1. ARC环境下
    以下代码会造成循环引用:
@property (nonatomic,strong) dBlock myBlock;
dBlock aBlock = ^{

        [self doSomething];

        NSLog(@"%p,%p,%@",&instanceObj,instanceObj, instanceObj);

    };

    self.myBlock=aBlock;
    //或者_myBlock=aBlock;

因为arc下,成员变量默认都是strong,引用外部对象的block会自动分配到堆区,为NSMallocBlock。所以,当成员变量或strong属性对blcok做强引用,便形成循环。
要解决循环引用可使用arc中的弱引用weak来间距引用,代码修改:

 __weak NSString* week_instanceObj=instanceObj;
 __weak typeof(self) weekself = self;

    NSLog(@"week_instanceObj:%p,%p,%@",&week_instanceObj,week_instanceObj, week_instanceObj);

    dBlock aBlock = ^{

        [weekself doSomething];
        NSLog(@"week_instanceObj:%p,%p,%@",&week_instanceObj,week_instanceObj, week_instanceObj);


    };

这时可不能跟MRC一样时候__block来间距引用,因为__block对象在分配到堆区时,创建出来取代原变量的快照依然会对self做强引用。__weak对象会在block分配到堆区时创建一份快照,该快照对引用对象依然是弱引用。

总结

block类型:取决于block是否引用了外部变量。未引用外部变量的为NSGlobalBlock
NSGlobalBlock:未引用外部变量,不在堆栈中,独立的程序块。
NSStackBlock:引用外部变量 ,位于栈内存,函数返回后Block将无效,存在于MRC环境,通过copy可转为NSMallocBlock分配到堆区,在ARC下自动转换;
NSMallocBlock:NSStackBlock copy得来, 位于堆内存。

对外部变量的引用,除了静态变量 、全局变量、NSStackBlock中的block变量外,会对引用的变量做一份快照,保存创建快照时相同的值,oc对象(指针变量)保存相同引用地址。

关于将block copy到堆区后(或者arc自动copy),对引用的变量的持有变化:
在MRC下,只有局部变量的引用对象计数会+1(快照retain),对成员变量或self的引用也会使self计数+1,__block变量在copy后会取代原变量,但不会再对引用对象做retain。
在ARC下,局部变量会再强引用一次(快照也是强引用),对__block变量不改变强引用个数(快照变量会取代原变量),对成员变量或self的直接引用会使对self再强引用一次。
不论是MRC还是ARC下,当block分配到堆区后,其创建出来的快照的释放转为自动释放,只要block还在,其快照的引用就在。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值