精通iOS开发--第15章 Grand Central Dispatch和后台处理之Block与Self的循环引用

来源:互联网 发布:社交网络 推荐 综述 编辑:程序博客网 时间:2024/06/05 16:25

BlockSelf的循环引用

01:众所周知若self中引用了Block块,而此Block块中又引用了Self则会造成循环引用,需要提醒的是即使在你的block代码中没有显式地出现"self",也有可能出现循环引用!只要你在block里用到了self所拥有的东西就有可能导致循环引用。如下面的代码就会造成循环引用,测试发现BlockTest对象使用后没有正常释放。

@interface BlockTest()

@property (nonatomic,copy)NSString *name;

@property (nonatomic,copy)void(^myBlock)(void);

@end


- (instancetype)init

{

     self = [superinit];

     if (self) {

        self.name =@"BlockTest";

        self.myBlock = ^{

            NSLog(@"%@",_name);

        };

        self.myBlock();

     }

    returnself;

}


-(void)dealloc

{

      NSLog(@"dealloc");

#if __has_feature(objc_arc)

      NSLog(@"ARC    %s",__func__);

#else

     [superdealloc];

     NSLog(@"MRC    %s",__func__);

#endif

}

若改成以下代码则可以正常释放:

- (instancetype)init

{

    self = [superinit];

    if (self) {

        self.name =@"BlockTest";

        NSString* str = self.name;

        self.myBlock = ^{

            //NSLog(@"%@",_name);

           NSLog(@"%@",str);

        };

        self.myBlock();

    }

    return self;

}


02:为了解决循环引用问题:

    ARC:可以使用

        __weak BlockTest *weakSelf = self

    或者

        __unsafe_unretained BlockTest *unsafeSelf = self;


    MRC:

        __block BlockTest *blockSelf = self;

        使用__unsafe_unretained也可以。

    注意:MRC__block是不会引起retain但在ARC__block则会引起retainARC中应该使用__weak__unsafe_unretained弱引用。__weak只能在iOS5以后使用。通过测试会发现,在ARC中加了__weak__unsafe_unretained的变量引入后,BlockTest可以正常执行dealloc方法,而不转换和用__block转换的变量都会引起循环引用。


03:验证02结论的测试代码:

#import <Foundation/Foundation.h>

typedef void(^blockT)();

@interface BlockTest : NSObject

@property (nonatomic,copy)blockT block;

@end

//———————————————

#import "BlockTest.h"

@interface BlockTest()

@property (nonatomic,strong)NSString *name;

@end


@implementation BlockTest


- (instancetype)init

{

     self = [superinit];

     if (self) {

         __blocktypeof(self) weekSelf =self;

        self.block = ^{

             weekSelf.name =@"BlockTest";

             NSLog(@"%@",weekSelf.name);

       };

    }

    returnself;

}



-(void)dealloc

 {

     NSLog(@"%s",__func__);

}

@end


测试:

- (void)viewDidLoad {

     [superviewDidLoad];

     // Do any additional setup after loading the view, typically from a nib.

     BlockTest *test = [[BlockTestalloc]init];

     NSLog(@"%p",test);

}


通过类似的测试可以发现

MRC下面使用__unsafe_unretained__block都可以避免这个问题(MRC下不能使用__weak关键字)。

ARC下使用__unsafe_unretained__weak可以免。


04:看下面代码:

//MRC环境下,下面的代码会崩溃,则至少可以说明MRC下,Block块对__block修饰的date没有进行保留,所以sleep(2.0)后,再使用date时造成了崩溃。

- (void)blockTest

{

    __blockNSDate *date = [NSDatedate];

    NSLog(@"%p %lu %@",date,[dateretainCount],date);

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        sleep(2.0);

        NSLog(@"%p %lu %@",date,[dateretainCount],date);

    });

    

    NSLog(@"%p %lu %@",date,[dateretainCount],date);

}


//ARC 环境下,下面的代码编译时会报错,date不可以在block中修改。

- (void)blockTest

{

    NSDate *date = [NSDatedate];

    NSLog(@"%p %@",date,date);

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        sleep(2.0);

        NSLog(@"%p %@",date,date);

        date = [NSDate date];

        NSLog(@"%p %@",date,date);

    });

    

    NSLog(@"%p %@",date,date);

}


//ARC 环境下,下面的代码不会报错也不会崩溃,date可以在block中修改,这至少可以说明sleep(2.0)date依然存在。

- (void)blockTest

{

    __blockNSDate *date = [NSDatedate];

    NSLog(@"%p %@",date,date);

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        sleep(2.0);

        NSLog(@"%p %@",date,date);

        date = [NSDatedate];

        NSLog(@"%p %@",date,date);

    });

    

    NSLog(@"%p %@",date,date);

}

/*

 2016-06-25 17:01:57.505 GCD[2125:284060] 0x7fab93c20c10 2016-06-25 09:01:56 +0000

 2016-06-25 17:01:58.056 GCD[2125:284060] 0x7fab93c20c10 2016-06-25 09:01:56 +0000

 2016-06-25 17:02:00.059 GCD[2125:284200] 0x7fab93c20c10 2016-06-25 09:01:56 +0000

 2016-06-25 17:02:00.822 GCD[2125:284200] 0x7fab93eaeb80 2016-06-25 09:02:00 +0000

 */


-(void)dealloc

{

#if __has_feature(objc_arc)

#else

    [superdealloc];

#endif

    NSLog(@"%s",__func__);

}


//MRC,使用下面的方式就可以解决MRC下程序崩溃的问题。因为date2没有被__block修饰,所以其生命周期一直保留到block块执行结束。

- (void)blockTest

{

    __blockNSDate *date = [NSDatedate];

    NSLog(@"%p %@",date,date);

    NSDate *date2 = date;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        sleep(2.0);

        NSLog(@"%p %@",date2,date2);

        date = [NSDatedate];

        NSLog(@"%p %@",date,date);

    });

    

    NSLog(@"%p %@",date,date);

}


05:下面来讨论一下用__block修饰普通的C变量会如何:

    如果一个程序块在执行过程中访问任何外部变量,那么该程序块被创建时,会进行一些特殊的设置工作,以允许程序块访问那些变量。这些变量所包含的值要么被复制(如一些普通的C类型变量),要么被保存(如OC对象),这样他们所包含的值就可以在程序块内部使用了(被生命周期修饰符修饰的变量,要分ARCMRC等具体讨论)。

    但如果想让一个程序块向一个外部定义的变量写入数据,这时用__block存储修饰符修饰这个变量即可。一个有趣的副作用是:__block修饰的变量(在MRC中)在程序块中使用时不会被复制(看下面的例子会发现,普通的C类型变量会被复制到堆中)或者保留(如上面的示例程序,在MRCBlock修饰的变量并没有被保留)


//ARC,下面的程序不会崩溃。

- (void)blockTest

{

    int intt2 =200;

    NSLog(@"1:%p",&intt2);

    __blockint intt = 100;

    NSLog(@"2:%p %i",&intt,intt);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        sleep(2.0);

        NSLog(@"3:%p %i",&intt,intt);

        intt = 200;

        NSLog(@"4:%p %i",&intt,intt);

    });

    

    int *inta =malloc(sizeof(int));

    NSLog(@"5:%p",inta);

    free(inta);

    

    NSLog(@"6:%p %i",&intt,intt);

}


2016-06-25 17:38:06.765 GCD[2742:304212] 1:0x7fff50a02a8c

2016-06-25 17:38:11.063 GCD[2742:304212] 2:0x7fff50a02a80 100    //可以看出inttintt2这两个都是在栈上面分配的空间

2016-06-25 17:38:15.624 GCD[2742:304212] 5:0x7f93da408e80

2016-06-25 17:38:15.624 GCD[2742:304253] 3:0x7f93da535ef8 100

2016-06-25 17:38:17.211 GCD[2742:304212] 6:0x7f93da535ef8 100

2016-06-25 17:38:29.243 GCD[2742:304253] 4:0x7f93da535ef8 200    //可以看出intt被在堆中重新复制了一遍,甚至在block块外面其地址也与开始时不同(下面还会讨论这种情况何时才会发生)。


注意看下面的例子:

//MRC,下面的程序同样不会崩溃。

- (void)blockTest

{

    

    

    int intt2 =200;

    NSLog(@"1:%p",&intt2);//这两个都是在栈上面分配的空间

    

    __blockint intt = 100;

    NSLog(@"2:%p %i",&intt,intt);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        sleep(2.0);

        NSLog(@"3:%p %i",&intt,intt);

        intt = 200;

        NSLog(@"4:%p %i",&intt,intt);

    });

    

    int *inta =malloc(sizeof(int));

    NSLog(@"5:%p",inta);

    free(inta);

    

    NSLog(@"6:%p %i",&intt,intt);

}


2016-06-25 17:42:08.125 GCD[2857:307005] 1:0x7fff5dc39a8c

2016-06-25 17:42:19.124 GCD[2857:307005] 2:0x7fff5dc39a80 100

2016-06-25 17:42:30.435 GCD[2857:307005] 5:0x7fc782554960

2016-06-25 17:42:35.086 GCD[2857:307005] 6:0x7fc78240ab38 100

2016-06-25 17:42:35.086 GCD[2857:307395] 3:0x7fc78240ab38 100

2016-06-25 17:42:43.869 GCD[2857:307005] MRC    -[BlockTest dealloc]

2016-06-25 17:42:46.275 GCD[2857:307395] 4:0x7fc78240ab38 200


MRCintt同样在堆中被重新复制了一遍。结和selfblock循环引用的讨论,可以得出如下结果:

MRC block块外面用__block修饰的变量,若是OC对象,则不会retainC类型变量会在堆中重新分配

ARC block块外面用__block修饰的变量,若是OC对象,会retainC类型变量会在堆中重新分配。


06//ARC注意看内存分配,与__block修饰时还是有很大不同

- (void)blockTest2

{

    int intt =100;

    NSLog(@"1:%p  %i",&intt,intt);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        sleep(2.0);

        NSLog(@"2:%p  %i",&intt,intt);

        //intt = 200;

        NSLog(@"3:%p  %i",&intt,intt);

    });

    NSLog(@"4:%p  %i",&intt,intt);

}


2016-06-25 17:59:46.987 GCD[3137:315124] 0x7f8beb693f30

2016-06-25 17:59:48.794 GCD[3137:315124] 1:0x7fff5b27ca8c  100

2016-06-25 17:59:48.795 GCD[3137:315124] 4:0x7fff5b27ca8c  100

2016-06-25 17:59:50.800 GCD[3137:315194] 2:0x7f8beb611820  100

2016-06-25 17:59:50.800 GCD[3137:315194] 3:0x7f8beb611820  100



__block修饰时423打印的地址是相通的,没有其修饰时14相同,23相同。


再看MRC


- (void)blockTest2

{

    int intt =100;

    NSLog(@"1:%p  %i",&intt,intt);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

        sleep(2.0);

        NSLog(@"2:%p  %i",&intt,intt);

        //intt = 200;

        NSLog(@"3:%p  %i",&intt,intt);

    });

    NSLog(@"4:%p  %i",&intt,intt);

}


2016-06-25 18:04:10.279 GCD[3234:317528] 1:0x7fff53c36a8c  100

2016-06-25 18:04:10.279 GCD[3234:317528] 4:0x7fff53c36a8c  100

2016-06-25 18:04:10.279 GCD[3234:317528] MRC    -[BlockTest dealloc]

2016-06-25 18:04:12.284 GCD[3234:317641] 2:0x7ffa2bd03b40  100

2016-06-25 18:04:12.284 GCD[3234:317641] 3:0x7ffa2bd03b40  100


MRC下与ARC相识14相同,23相同



总结如下:对于C类型变量:在MRC中不能用__weak,__unsafe_unretained__strong__autoreleasing等修饰。

无论MRC还是ARC 若使用__block进行修饰,则只要在block块中使用该变量(如下面两个例子,只有使用__block修饰该变量,且在Block块中使用了该变量,即使没有调用该Block就会出现下面的描述),该变量会被在堆中重新复制,即使在block块之外也重新分配到了新的位置。


- (void)blockTest2

{

    __blockint intt = 100;

    NSLog(@"1:%p  %i",&intt,intt);

//    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

//        sleep(2.0);

//        NSLog(@"2:%p  %i",&intt,intt);

//        //intt = 200;

//        NSLog(@"3:%p  %i",&intt,intt);

//    });

    NSLog(@"4:%p  %i",&intt,intt);

}



2016-06-25 18:13:12.249 GCD[3411:322184] 1:0x7fff5aa8da88  100

2016-06-25 18:13:12.249 GCD[3411:322184] 4:0x7fff5aa8da88  100

2016-06-25 18:13:12.249 GCD[3411:322184] dealloc

2016-06-25 18:13:13.794 GCD[3411:322184] ARC    -[BlockTest dealloc]



- (void)blockTest2

{

    __blockint intt = 100;

    NSLog(@"1:%p  %i",&intt,intt);

    

    dispatch_block_t block = ^{

        sleep(2.0);

        NSLog(@"2:%p  %i",&intt,intt);

        NSLog(@"3:%p  %i",&intt,intt);

    };

    //dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);

    

    NSLog(@"4:%p  %i",&intt,intt);

}


2016-06-25 18:16:43.413 GCD[3491:324296] 1:0x7fff5c522a88  100

2016-06-25 18:16:44.593 GCD[3491:324296] 4:0x7fe1d3e8eb88  100

2016-06-25 18:16:44.593 GCD[3491:324296] dealloc

2016-06-25 18:16:44.594 GCD[3491:324296] ARC    -[BlockTest dealloc]


07:看下面的代码

- (void)blockTest2

{

    __blockint intt = 100;

    NSLog(@"1:%p  %i",&intt,intt);

    

    dispatch_block_t block = ^{

        sleep(2.0);

        NSLog(@"2:%p  %i",&intt,intt);

        NSLog(@"3:%p  %i",&intt,intt);

        NSLog(@"%@",self.name);

    };

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    

    NSLog(@"4:%p  %i",&intt,intt);

}


2016-06-25 19:34:39.947 GCD[3643:334264] 1:0x7fff5aaeba88  100

2016-06-25 19:34:39.948 GCD[3643:334264] 4:0x7fade1c083d8  100

2016-06-25 19:34:41.950 GCD[3643:334315] 2:0x7fade1c083d8  100

2016-06-25 19:34:41.950 GCD[3643:334315] 3:0x7fade1c083d8  100

2016-06-25 19:34:50.096 GCD[3643:334315] BlockTest

2016-06-25 19:34:50.097 GCD[3643:334315] dealloc

2016-06-25 19:34:50.097 GCD[3643:334315] ARC    -[BlockTest dealloc]


观察发现block块中使用到了self但,self并没有持有block块,所以最后self还是正常释放了。


08:MyArry的实现见最后

//ARC

- (void)blockTest

{

    MyArry *obj1 = [[MyArryalloc]init];

    [obj1 addObject:@"0"];

    

#if __has_feature(objc_arc)

    NSLog(@"1:%p %@",obj1,obj1);

#else

    NSLog(@"1:%p %@    %lu",obj1,obj1,obj1.retainCount);

#endif

    

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        

        sleep(2.0);

        [obj1 addObject:@"2"];

#if __has_feature(objc_arc)

        NSLog(@"2:%p %@",obj1,obj1);

#else

        NSLog(@"2:%p %@    %lu",obj1,obj1,obj1.retainCount);

#endif

    };

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    sleep(1.0);

#if __has_feature(objc_arc)

    NSLog(@"3:%p %@",obj1,obj1);

#else

    NSLog(@"3:%p %@    %lu",obj1,obj1,obj1.retainCount);

#endif

}


2016-06-25 20:10:58.879 GCD[4171:348666] 1:0x7fef43604620 (

    0

)

2016-06-25 20:10:59.882 GCD[4171:348666] 3:0x7fef43604620 (

    0,

    1

)

2016-06-25 20:11:00.886 GCD[4171:348718] 2:0x7fef43604620 (

    0,

    1,

    2

)

2016-06-25 20:11:00.886 GCD[4171:348718] MyArry

2016-06-25 20:11:00.887 GCD[4171:348718] ARC    -[MyArry dealloc]


一切正常,obj1正常释放,block块执行也没有崩溃,说明ARC下面obj1被保存。


09:ARC:

- (void)record:(NSInteger)inte myArry:(MyArry*)obj1

{

#if __has_feature(objc_arc)

    NSLog(@"%li %p %@",inte,obj1,obj1);

#else

    NSLog(@"%li %p %@    %lu",inte,obj1,obj1,obj1.retainCount);

#endif

}


//ARC

- (void)blockTest

{

    __blockMyArry *obj1 = [[MyArryalloc]init];

    [obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

        

        obj1 = [[MyArryalloc]init];

        [obj1 addObject:@"2"];

        [selfrecord:4myArry:obj1];

    };

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    sleep(1.0);

    [obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

}


2016-06-25 20:20:55.569 GCD[4356:353057] 1 0x7fc112c88f30 (

    0

)

2016-06-25 20:20:55.569 GCD[4356:353105] 2 0x7fc112c88f30 (

    0,

    1

)

2016-06-25 20:20:56.571 GCD[4356:353057] 5 0x7fc112c88f30 (

    0,

    1,

    3

)

2016-06-25 20:20:57.572 GCD[4356:353105] 3 0x7fc112c88f30 (

    0,

    1,

    3

)

2016-06-25 20:20:57.572 GCD[4356:353105] MyArry

2016-06-25 20:20:57.572 GCD[4356:353105] ARC    -[MyArry dealloc]    //[selfrecord:3myArry:obj1];后面 obj1被重新赋值,原来指向的对象正常释放。

2016-06-25 20:20:57.573 GCD[4356:353105] 4 0x7fc112e1af80 (

    2

)

2016-06-25 20:20:57.573 GCD[4356:353105] MyArry

2016-06-25 20:20:57.573 GCD[4356:353105] ARC    -[MyArry dealloc]


10:ARC

- (void)record:(NSInteger)inte myArry:(MyArry*)obj1

{

#if __has_feature(objc_arc)

    NSLog(@"%li %p %@",inte,obj1,obj1);

#else

    NSLog(@"%li %p %@    %lu",inte,obj1,obj1,obj1.retainCount);

#endif

}


//ARC

- (void)blockTest

{

    MyArry *obj0 = [[MyArryalloc]init];

    __weakMyArry *obj1 = obj0;

    [obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    sleep(1.0);

    [obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

}


2016-06-25 20:23:03.586 GCD[4394:354093] 1 0x7ff168c0f5c0 (

    0

)

2016-06-25 20:23:03.587 GCD[4394:354151] 2 0x7ff168c0f5c0 (

    0,

    1

)

2016-06-25 20:23:04.587 GCD[4394:354093] 5 0x7ff168c0f5c0 (

    0,

    1,

    3

)

2016-06-25 20:23:04.588 GCD[4394:354093] MyArry

2016-06-25 20:23:04.588 GCD[4394:354093] ARC    -[MyArry dealloc]

2016-06-25 20:23:05.592 GCD[4394:354151] 3 0x0 (null)    //注意看,ARC下,若使用__weak此处打印的时null,这正是我们预料的。



11:ARC


- (void)record:(NSInteger)inte myArry:(MyArry*)obj1

{

#if __has_feature(objc_arc)

    NSLog(@"%li %p %@",inte,obj1,obj1);

#else

    NSLog(@"%li %p %@    %lu",inte,obj1,obj1,obj1.retainCount);

#endif

}


//ARC

- (void)blockTest

{

    MyArry *obj0 = [[MyArryalloc]init];

    __unsafe_unretainedMyArry *obj1 = obj0;

    [obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    sleep(1.0);

    [obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

}


2016-06-25 20:24:23.913 GCD[4421:354788] 1 0x7fbdfbc134f0 (

    0

)

2016-06-25 20:24:23.921 GCD[4421:354830] 2 0x7fbdfbc134f0 (

    0,

    1

)

2016-06-25 20:24:24.922 GCD[4421:354788] 5 0x7fbdfbc134f0 (

    0,

    1,

    3

)

2016-06-25 20:24:24.922 GCD[4421:354788] MyArry

2016-06-25 20:24:24.923 GCD[4421:354788] ARC    -[MyArry dealloc]

(lldb) 


程序崩溃,ARC下,若使用__unsafe_unretained,则程序如预期的崩溃了。



12ARC本想用__autoreleasing修饰MyArry *obj1,结果直接编译错误。



13:下面看MRC


- (void)record:(NSInteger)inte myArry:(MyArry*)obj1

{

#if __has_feature(objc_arc)

    NSLog(@"%li %p %@",inte,obj1,obj1);

#else

    NSLog(@"%li %p %@    %lu",inte,obj1,obj1,obj1.retainCount);

#endif

}


//MRC

- (void)blockTest

{

    MyArry *obj1 = [[MyArryalloc]init];

    [obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    sleep(1.0);

    [obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

    [obj1 release];

}



2016-06-25 20:28:29.522 GCD[4506:357617] 1 0x7f8fe2d8fa00 (

    0

)    1

2016-06-25 20:28:29.523 GCD[4506:357657] 2 0x7f8fe2d8fa00 (

    0,

    1

)    2

2016-06-25 20:28:30.524 GCD[4506:357617] 5 0x7f8fe2d8fa00 (

    0,

    1,

    3

)    2

2016-06-25 20:28:31.527 GCD[4506:357657] 3 0x7f8fe2d8fa00 (

    0,

    1,

    3

)    1

2016-06-25 20:28:31.528 GCD[4506:357657] MyArry

2016-06-25 20:28:31.528 GCD[4506:357657] ARC    -[MyArry dealloc] //一切正常,此处打印的ARC,是因为MyArry是用ARC管理的,但测试代码是MRC



14MRC

- (void)record:(NSInteger)inte myArry:(MyArry*)obj1

{

#if __has_feature(objc_arc)

    NSLog(@"%li %p %@",inte,obj1,obj1);

#else

    NSLog(@"%li %p %@    %lu",inte,obj1,obj1,obj1.retainCount);

#endif

}


//MRC

- (void)blockTest

{

    __blockMyArry *obj1 = [[MyArryalloc]init];

    [obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    sleep(1.0);

    [obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

    [obj1 release];

}


2016-06-25 20:31:30.899 GCD[4584:359488] 1 0x7ffd12d03220 (

    0

)    1

2016-06-25 20:31:30.900 GCD[4584:359529] 2 0x7ffd12d03220 (

    0,

    1

)    1

2016-06-25 20:31:31.901 GCD[4584:359488] 5 0x7ffd12d03220 (

    0,

    1,

    3

)    1

2016-06-25 20:31:31.902 GCD[4584:359488] MyArry

2016-06-25 20:31:31.902 GCD[4584:359488] ARC    -[MyArry dealloc]

程序崩溃,如预料的一样,程序崩溃了。



15:下面看一下MRC__unsafe_unretained修饰会如何



//MRC

- (void)blockTest

{

    __unsafe_unretainedMyArry *obj1 = [[MyArryalloc]init];

    [obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    sleep(1.0);

    [obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

    [obj1 release];

}


本来预计会崩溃,实际上运转正常


2016-06-25 20:35:45.911 GCD[4646:361755] 1 0x7fb393e0ab20 (

    0

)    1

2016-06-25 20:35:45.912 GCD[4646:361797] 2 0x7fb393e0ab20 (

    0,

    1

)    2

2016-06-25 20:35:46.913 GCD[4646:361755] 5 0x7fb393e0ab20 (

    0,

    1,

    3

)    2

2016-06-25 20:35:47.917 GCD[4646:361797] 3 0x7fb393e0ab20 (

    0,

    1,

    3

)    1

2016-06-25 20:35:47.917 GCD[4646:361797] MyArry

2016-06-25 20:35:47.918 GCD[4646:361797] ARC    -[MyArry dealloc]


说明__unsafe_unretained不能用于MRC下解决selfblock块的循环引用问题,因为他也会使引用计数+1(其实后面测试会发现,这是由于GCD引起的,__unsafe_unretained可以用于MRC下解决selfblock块之间的循环引用问题);



———————————————————————

//

//  MyArry.h

//  GCD

//

//  Created by ranzhou on 16/6/25.

//  Copyright © 2016 ranzhouee. All rights reserved.

//


#import <Foundation/Foundation.h>


@interface MyArry :NSMutableArray


@end


———————————————————————



//

//  MyArry.m

//  GCD

//

//  Created by ranzhou on 16/6/25.

//  Copyright © 2016 ranzhouee. All rights reserved.

//


#import "MyArry.h"

#import <Foundation/NSArray.h>

@interface MyArry()

@property (nonatomic,strong) NSMutableArray *arry;

@end


@implementation MyArry


- (instancetype)initWithCapacity:(NSUInteger)numItems

{

    self = [super initWithCapacity:numItems];

    if (self) {

        self.arry = [[NSMutableArray alloc]initWithCapacity:numItems];

    }

    returnself;

}



- (instancetype)init

{

    self = [super init];

    if (self) {

        self.arry = [[NSMutableArray alloc]init];

    }

    returnself;

}


-(NSUInteger)count

{

    returnself.arry.count;

}


- (void)insertObject:(id)anObject atIndex:(NSUInteger)index

{

    [self.arryinsertObject:anObject atIndex:index];

}


- (id)objectAtIndex:(NSUInteger)index

{

    return [self.arryobjectAtIndex:index];

}


-(NSString*)description

{

    return [self.arrydescription];

}


- (void)dealloc

{

    NSLog(@"%@",NSStringFromClass([selfclass]));

#if __has_feature(objc_arc)

    NSLog(@"ARC    %s",__func__);

#else

    [super dealloc];

    self.arry = nil;

    NSLog(@"MRC    %s",__func__);

#endif

}


@end



———————————————————————


16:再次验证15的结论:


- (instancetype)init

{

    self = [superinit];

    if (self) {

        self.name =@"BlockTest";//__strong __weak(编译错误) __autoreleasing

        __unsafe_unretainedtypeof(self) anSelf =self;

        self.myBlock = ^{

            NSLog(@"%@",anSelf.name);

        };

    }

    returnself;

}


2016-06-25 20:46:26.378 GCD[4900:367521] <BlockTest: 0x7fac43c05860>

2016-06-25 20:46:26.379 GCD[4900:367521] dealloc

2016-06-25 20:46:26.379 GCD[4900:367521] MRC    -[BlockTest dealloc]

__block __unsafe_unretained均有效果



17:很奇怪::

下面两个.m文件中的代码均为MRC环境:




//

//  BlockTest.m

//  GCD

//

//  Created by ranzhou on 16/6/25.

//  Copyright © 2016 ranzhouee. All rights reserved.

//


#import "BlockTest.h"


@interface BlockTest()

@property (nonatomic,copy)NSString *name;

@property (nonatomic,copy)void(^myBlock)(void);

@end


@implementation BlockTest


- (instancetype)init

{

    self = [superinit];

    if (self) {

        self.name =@"BlockTest";//__strong __weak(编译错误) __autoreleasing

        __unsafe_unretainedtypeof(self) anSelf =self;

        NSLog(@"1:%lu",self.retainCount);

        self.myBlock = ^{

            NSLog(@"%@",anSelf.name);

            NSLog(@"2:%lu",anSelf.retainCount);

        };

        NSLog(@"3:%lu",self.retainCount);

        self.myBlock();

        NSLog(@"4:%lu",self.retainCount);

    }

    returnself;

}


-(void)dealloc

{

    NSLog(@"dealloc");

#if __has_feature(objc_arc)

    NSLog(@"ARC    %s",__func__);

#else

    [superdealloc];

    NSLog(@"MRC    %s",__func__);

#endif

}



- (void)blockTest2

{

    BlockTest *obj0 = [[BlockTestalloc]init];

    

    __unsafe_unretainedBlockTest *obj1 = obj0;

    //[obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        //[obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

    sleep(1.0);

    //[obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

    [obj0 release];

}



- (void)record:(NSInteger)inte myArry:(NSObject*)obj1

{

#if __has_feature(objc_arc)

    NSLog(@"%li %p %@",inte,obj1,obj1);

#else

    NSLog(@"%li %p %@    %lu",inte,obj1,obj1,obj1.retainCount);

#endif

}


测试代码:

    BlockTest *bloc = [[BlockTestalloc]init];

    [bloc release];

    

    [selfblockTest2];



日志:

2016-06-25 21:04:15.713 GCD[5428:378544] 1:1

2016-06-25 21:04:15.714 GCD[5428:378544] 3:1

2016-06-25 21:04:15.714 GCD[5428:378544] BlockTest

2016-06-25 21:04:15.714 GCD[5428:378544] 2:1

2016-06-25 21:04:15.714 GCD[5428:378544] 4:1

2016-06-25 21:04:15.714 GCD[5428:378544] dealloc

2016-06-25 21:04:15.715 GCD[5428:378544] MRC    -[BlockTest dealloc]

2016-06-25 21:04:15.715 GCD[5428:378544] 1:1

2016-06-25 21:04:15.715 GCD[5428:378544] 3:1

2016-06-25 21:04:15.715 GCD[5428:378544] BlockTest

2016-06-25 21:04:15.715 GCD[5428:378544] 2:1

2016-06-25 21:04:15.715 GCD[5428:378544] 4:1

2016-06-25 21:04:15.715 GCD[5428:378544] 1 0x7fc8e26bbf80 <BlockTest: 0x7fc8e26bbf80>    1

2016-06-25 21:04:15.716 GCD[5428:378614] 2 0x7fc8e26bbf80 <BlockTest: 0x7fc8e26bbf80>    2

2016-06-25 21:04:16.716 GCD[5428:378544] 5 0x7fc8e26bbf80 <BlockTest: 0x7fc8e26bbf80>    2

2016-06-25 21:04:17.721 GCD[5428:378614] 3 0x7fc8e26bbf80 <BlockTest: 0x7fc8e26bbf80>    1

2016-06-25 21:04:17.721 GCD[5428:378614] dealloc

2016-06-25 21:04:17.721 GCD[5428:378614] MRC    -[BlockTest dealloc]


很奇怪(注意看引用计数)。。。。



把上面的方法改一改:

- (void)blockTest2

{

    BlockTest *obj0 = [[BlockTestalloc]init];

    

    __unsafe_unretainedBlockTest *obj1 = obj0;

    //[obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        //[obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    

    block();

    sleep(1.0);

    //[obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

    [obj0 release];

}


则:


2016-06-25 21:11:36.478 GCD[5544:382236] 1:1

2016-06-25 21:11:36.479 GCD[5544:382236] 3:1

2016-06-25 21:11:36.479 GCD[5544:382236] BlockTest

2016-06-25 21:11:36.479 GCD[5544:382236] 2:1

2016-06-25 21:11:36.479 GCD[5544:382236] 4:1

2016-06-25 21:11:36.479 GCD[5544:382236] dealloc

2016-06-25 21:11:36.480 GCD[5544:382236] MRC    -[BlockTest dealloc]

2016-06-25 21:11:36.480 GCD[5544:382236] 1:1

2016-06-25 21:11:36.480 GCD[5544:382236] 3:1

2016-06-25 21:11:36.480 GCD[5544:382236] BlockTest

2016-06-25 21:11:36.480 GCD[5544:382236] 2:1

2016-06-25 21:11:36.480 GCD[5544:382236] 4:1

2016-06-25 21:11:56.287 GCD[5544:382236] 1 0x7ffe21e02240 <BlockTest: 0x7ffe21e02240>    1

2016-06-25 21:11:56.897 GCD[5544:382236] 2 0x7ffe21e02240 <BlockTest: 0x7ffe21e02240>    1

2016-06-25 21:12:00.566 GCD[5544:382236] 3 0x7ffe21e02240 <BlockTest: 0x7ffe21e02240>    1

2016-06-25 21:12:03.358 GCD[5544:382236] 5 0x7ffe21e02240 <BlockTest: 0x7ffe21e02240>    1

2016-06-25 21:12:03.358 GCD[5544:382236] dealloc

2016-06-25 21:12:03.358 GCD[5544:382236] MRC    -[BlockTest dealloc]



可能是由于使用GCD的缘故:

个人觉得因为使用了GCD,所有Block块可能在变量声明的作用域外面被使用,所以对block进行了retain操作。

也有可能是因为这个block块作为函数的参数被使用,所以blockretain

验证:


18


//MRC

- (void)blockTest1

{

    MyArry *obj0 = [[MyArryalloc]init];

    

    __unsafe_unretainedMyArry *  obj1 = obj0;

    [obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    [selffun:block];

    sleep(1.0);

    [obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

    [obj0 release];

}




- (void)fun:(myBlock)block

{

    //dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);

    /**

     2016-06-25 21:25:03.481 GCD[5753:387861] 1 0x7fc728c25ad0 (

     0

     )    1

     2016-06-25 21:25:03.482 GCD[5753:387915] 2 0x7fc728c25ad0 (

     0,

     1

     )    2

     2016-06-25 21:25:04.482 GCD[5753:387861] 5 0x7fc728c25ad0 (

     0,

     1,

     3

     )    2

     2016-06-25 21:25:05.485 GCD[5753:387915] 3 0x7fc728c25ad0 (

     0,

     1,

     3

     )    1

     2016-06-25 21:25:05.486 GCD[5753:387915] MyArry

     2016-06-25 21:25:05.486 GCD[5753:387915] ARC    -[MyArry dealloc]

     */

}


- (void)fun:(myBlock)block

中若直接调用block(),则打印的引用计数是正常的,果然和GCD有关系。



19

下面这段代码在MRC下运行良好,而在ARC下则会崩溃:

- (void)record:(NSInteger)inte myArry:(NSObject*)obj1

{

#if __has_feature(objc_arc)

    NSLog(@"%li %p %@",inte,obj1,obj1);

#else

    NSLog(@"%li %p %@    %lu",inte,obj1,obj1,obj1.retainCount);

#endif

}


- (void)fun:(myBlock)block

{

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), block);

}



- (void)blockTest1

{

    MyArry *obj0 = [[MyArryalloc]init];

    

    __unsafe_unretainedMyArry *  obj1 = obj0;

    [obj1 addObject:@"0"];

    [selfrecord:1myArry:obj1];

    

    dispatch_block_t block = ^{

        [obj1 addObject:@"1"];

        [selfrecord:2myArry:obj1];

        

        sleep(2.0);

        [selfrecord:3myArry:obj1];

    };

    [selffun:block];

    sleep(1.0);

    [obj1 addObject:@"3"];

    [selfrecord:5myArry:obj1];

    

#if __has_feature(objc_arc)

    NSLog(@"ARC");

#else

    [obj0 release];

    NSLog(@"MRC");

#endif

    

}


20

下面的这段代码不会引起循环引用,开头刚刚讨论过

- (instancetype)init

{

    self = [superinit];

    if (self) {

        self.name =@"BlockTest";

        NSLog(@"1:%lu",self.retainCount);

        

        NSString *str =self.name;

        self.myBlock = ^{

            NSLog(@"%@",str);

        };

        NSLog(@"3:%lu",self.retainCount);

        self.myBlock();

        NSLog(@"4:%lu",self.retainCount);

    }

    returnself;

}


2016-06-25 21:34:57.999 GCD[6005:393794] 1:1

2016-06-25 21:34:58.000 GCD[6005:393794] 3:1

2016-06-25 21:34:58.000 GCD[6005:393794] BlockTest

2016-06-25 21:34:58.000 GCD[6005:393794] 4:1

2016-06-25 21:34:58.000 GCD[6005:393794] dealloc

2016-06-25 21:34:58.000 GCD[6005:393794] MRC    -[BlockTest dealloc]



至此依然有很多疑问:

1:为什么声明block属性时要使用copy;

2:为什么MRC中用__unsafe_unretained修饰的变量在Block块中使用时没有崩溃(具体见上面的示例)。

3:程序是如何复制在block中使用的__block修饰的C类型变量的。

……

0 0
原创粉丝点击