iOS Block

block类型

根据Block在内存中的位置,系统把Block分为3类:NSGlobalBlock,NSStackBlock, NSMallocBlock;

  • NSGlobalBlock(_NSConcreteGlobalBlock):位于内存全局区

只要没有对block外的变量进行引用,创建后的block就是NSGlobalBlock。相当于普通的函数,在内存中存在于程序代码段。

  • NSStackBlock(_NSConcreteStackBlock):位于内存栈区

只要block对外部的变量进行引用的话,默认创建的就是NSStackBlock。

  • NSMallocBlock(_NSConcreteMallocBlock):位于内存堆区

只有对NSStackBlock进行copy才会得到NSMallocBlock。NSMallocBlock在内存中存在堆中。在ARC的情况下会自动对NSStackBlock进行copy


//在ARC下很少出现NSStackBlock,因为 arc 下会自动往堆内存中copy,只有weak的时候才不会copy

int (^globalBlock)(int) = ^(int a){return a*a};

int v = 10;
int (^mallocBlock)(int) = ^(int a){return a*v};

__weak int (^stackBlock)(int) = ^(int a){return a*v};
NSLog(@"stackBlock %@", stackBlock);

对block自身内存的管理

  • 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],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
  • NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
  • 尽量不要对Block使用retain操作。
  • Block被另一个Block使用时,另一个Block被copy到堆上时,被使用的Block也会被copy。但作为参数的Block是不会发生copy的。
  • 当 block 作为参数被传入方法名带有 usingBlock 的 Cocoa Framework 方法或 GCD 的 API 时。这些方法会在内部对传递进来的 block 调用 copy 或 _Block_copy 进行拷贝。

循环引用

  • __block:在非ARC中使用,NSMallocBlock类型的block不会对__block修饰的的变量引用计数+1,从而消除循环引用;在ARC中使用__block无效
  • __weak:在ARC中使用,作用和__block一样,从而消除循环引用;在非ARC中不可以使用__weak;

系统API的循环引用

系统的某些block api中,UIView的block版本写动画时不需要考虑,但也有一些api需要考虑:

  • 所谓“引用循环”是指双向的强引用,所以那些“单向的强引用”(block 强引用 self )没有问题,比如这些:

[UIView animateWithDuration:duration animations:^{ [self.superview layoutIfNeeded];}]; 
                                                    

[[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.someProperty = xyz;}];


[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification" 
            object:nil 
             queue:[NSOperationQueue mainQueue]
        usingBlock:^(NSNotification * notification) {
                                            self.someProperty = xyz; }]; 

  • 但如果你使用一些参数中可能含有 ivar 的系统 api ,如 GCD 、NSNotificationCenter就要小心一点:比如GCD 内部如果引用了 self,而且 GCD 的其他参数是 ivar,则要考虑到循环引用:
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doSomethingElse];
} );

类似的:

__weak __typeof__(self) weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
                                                                object:nil
                                                                 queue:nil
                                                            usingBlock:^(NSNotification *note) {
      __typeof__(self) strongSelf = weakSelf;
      [strongSelf dismissModalViewControllerAnimated:YES];
}];
  
self --> _observer --> block --> self 显然这也是一个循环引用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 第一部分:Block本质 Q:什么是Block,Block的本质是什么? block本质上也是一个OC对象,它内部...
    sheldon_龙阅读 3,590评论 0 0
  • 参考篇:iOS-Block浅谈 前言:本文简述Block本质,如有错误请留言指正。 第一部分:Block本质 Q:...
    梦蕊dream阅读 61,686评论 41 323
  • 1、block简介 block字面意思就是代码块 iOS4.0 Apple引入的特性 block是Objectiv...
    呆子四二阅读 5,526评论 1 6
  • 所有的Block里面的self必须要weak一下? 很显然答案不都是,有些情况下是可以直接使用self的,比如调用...
    恋空K阅读 4,026评论 0 3
  • 产后最明显的一点是我前半分钟在抱怨,后半分钟在自责。比如:娃闹觉我烦,今天忍不住冲她发脾气了,过后我又难过了,一直...
    許小暖阅读 1,814评论 0 0

友情链接更多精彩内容