您好,欢迎来电子发烧友网! ,新用户?[免费注册]

您的位置:电子发烧友网>源码下载>通讯/手机编程>

block是如何持有对象的

大小:0.3 MB 人气: 2017-09-26 需要积分:1

  这篇文章主要会介绍 block 是如何持有并且释放对象的。文章中的代码都出自 Facebook 开源的用于检测循环引用的框架 FBRetainCycleDetector。

  为什么会谈到 block

  可能很多读者会有这样的疑问,本文既然是对 FBRetainCycleDetector 解析的文章,为什么会提到 block?原因其实很简单,因为在 iOS 开发中大多数的循环引用都是因为 block 使用不当导致的,由于 block 会 retain 它持有的对象,这样就很容易造成循环引用,最终导致内存泄露。

  在 FBRetainCycleDetector 中存在这样一个类 FBObjectiveCBlock,这个类的 - allRetainedObjects 方法就会返回所有 block 持有的强引用,这也是文章需要关注的重点。

  - (NSSet *)allRetainedObjects {

  NSMutableArray *results = [[[super allRetainedObjects] allObjects] mutableCopy];

  __attribute__((objc_precise_lifetime)) id anObject = self.object;

  void *blockObjectReference = (__bridge void *)anObject;

  NSArray *allRetainedReferences = FBGetBlockStrongReferences(blockObjectReference);

  for (id object in allRetainedReferences) {

  FBObjectiveCGraphElement *element = FBWrapObjectGraphElement(self, object, self.configuration);

  if (element) {

  [results addObject:element];

  }

  }

  return [NSSet setWithArray:results];

  }

  这部分代码中的大部分都不重要,只是在开头调用父类方法,在最后将获取的对象包装成一个系列 FBObjectiveCGraphElement,最后返回一个数组,也就是当前对象 block 持有的全部强引用了。

  Block 是什么?

  对 block 稍微有了解的人都知道,block 其实是一个结构体,其结构大概是这样的:

  struct BlockLiteral {

  void *isa;

  int flags;

  int reserved;

  void (*invoke)(void *, 。。.);

  struct BlockDeor *deor;

  };

  struct BlockDeor {

  unsigned long int reserved;

  unsigned long int size;

  void (*copy_helper)(void *dst, void *src);

  void (*dispose_helper)(void *src);

  const char *signature;

  };

  在 BlockLiteral 结构体中有一个 isa 指针,而对 isa了解的人也都知道,这里的 isa 其实指向了一个类,每一个 block 指向的类可能是 __NSGlobalBlock__、__NSMallocBlock__ 或者 __NSStackBlock__,但是这些 block,它们继承自一个共同的父类,也就是 NSBlock,我们可以使用下面的代码来获取这个类:

  static Class _BlockClass() {

  static dispatch_once_t onceToken;

  static Class blockClass;

  dispatch_once(&onceToken, ^{

  void (^testBlock)() = [^{} copy];

  blockClass = [testBlock class];

  while(class_getSuperclass(blockClass) && class_getSuperclass(blockClass) != [NSObject class]) {

  blockClass = class_getSuperclass(blockClass);

  }

  [testBlock release];

  });

  return blockClass;

  }

  Objective-C 中的三种 block __NSMallocBlock__、__NSStackBlock__ 和 __NSGlobalBlock__ 会在下面的情况下出现:

  block是如何持有对象的

  在 ARC 中,捕获外部了变量的 block 的类会是 __NSMallocBlock__ 或者 __NSStackBlock__,如果 block 被赋值给了某个变量在这个过程中会执行 _Block_copy 将原有的 __NSStackBlock__ 变成 __NSMallocBlock__;但是如果 block 没有被赋值给某个变量,那它的类型就是 __NSStackBlock__;没有捕获外部变量的 block 的类会是 __NSGlobalBlock__ 即不在堆上,也不在栈上,它类似 C 语言函数一样会在代码段中。

  在非 ARC 中,捕获了外部变量的 block 的类会是 __NSStackBlock__,放置在栈上,没有捕获外部变量的 block 时与 ARC 环境下情况相同。

非常好我支持^.^

(1) 100%

不好我反对

(0) 0%

      发表评论

      用户评论
      评价:好评中评差评

      发表评论,获取积分! 请遵守相关规定!