iOS Block

作者: ThaiLanKing | 来源:发表于2017-07-09 21:52 被阅读6次

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 显然这也是一个循环引用。

相关文章

网友评论

    本文标题:iOS Block

    本文链接:https://www.haomeiwen.com/subject/phuehxtx.html