当前位置:首页>优优资讯 > 软件教程 > 电脑软件教程 > iOS开发:Block基础介绍和内存管理

iOS开发:Block基础介绍和内存管理

作者:本站整理 时间:2015-09-15


  基础介绍:

  一、概述
 
  Block是C级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block。
 
  二、热身
 
  先看一个比较简单的Block例子:
 
  int multiplier = 7;
  int (^myBlock)(int) = ^(int num) {
  return num * multiplier;
  };
 
  在这个例子中,myBlock是一个Block变量,它接受一个int类型的参数,返回一个int类型的值。是不是很像C函数?
 
  来,让我们typedef一下
 
  typedef void (^BoolBlock)(BOOL);//一个只接受一个BOOL参数,没有返回值的block
  typedef int (^IntBlock)(void);//一个没有参数,返回int的block
  typedef BoolBlock (^HugeBlock)(IntBlock);//看看,这个HugeBlock的参数和返回值都是block
 
  三、更详细的例子
 
  注意:上面的typedef都还有效~
 
  主动调用一下:
 
  - (void)someMethod
  {
  BoolBlock ablock = ^(BOOL bValue) {
  NSLog(@"Bool block!");
  };
  ablock();
  }
 
  作为参数返回:
 
  typedef void (^BoolBlock)(BOOL);
  - (BoolBlock)foo()
  {
  BoolBlock ablock = ^(BOOL bValue) {
  NSLog(@"Bool block!");
  };
  return [[ablock copy] autorelease];//一定要copy,将其复制到堆上,更详细的原理,将在后续章节讲解
  }
 
  类的一个成员:
 
  @interface OBJ1 : NSObject
  @property (nonatomic, copy)BoolBlock block;//理由同上啊,同学们
  @end
  OBJ1 *obj1 = ...
  obj1.block = ^(BOOL bValue) {
  NSLog(@"Bool block!");
  };
 
  其他函数的参数:
 
  - (void)foo(BoolBlock block)
  {
  if (block) {
  block();
  }
  }
 
  甚至其他block的参数:
 
  BoolBlock bBlock = ^(BOOL bV){if(Bv){/*do some thing*/}};
  HugeBlock hBlock = ^(BoolBlock bB) {bB();};
  hBolck(bBlock);
 
  啊,全局变量!:
 
  static int(^maxIntBlock)(int, int) = ^(int a, int b){return a>b?a:b;};
  int main()
  {
  printf("%d\n", maxIntBlock(2,10));
  return 0;
  }
 
  好了,你知道block大概能怎么用了。
 
  四,特殊的标记,__block
 
  如果要在block内修改block外声明的栈变量,那么一定要对该变量加__block标记:
 
  int main()
  {
  __block int i = 1024;
  BoolBlock bBlock = ^(BOOL bV) {
  if (bV) {
  i++;//如果没有__block标记,是无法通过编译的。
  }
  };
  }

  内存管理与其他特性:
 
  一、block放在哪里
 
  我们针对不同情况来讨论block的存放位置:
 
  1.栈和堆
 
  以下情况中的block位于堆中:
 
  void foo()
  {
  __block int i = 1024;
  int j = 1;
  void (^blk)(void);
  void (^blkInHeap)(void);
  blk = ^{ printf("%d, %d\n", i, j);};//blk在栈里
  blkInHeap = Block_copy(blk);//blkInHeap在堆里
  }
  - (void)fooBar
  {
  _oi = 1;
  OBJ1* oj = self;
  void (^oblk)(void) = ^{ printf("%d\n", oj.oi);};
  void (^oblkInHeap)(void) = [oblk copy];//oblkInHeap在堆中
  }
 
  2.全局区
 
  以下情况中的block位于全局区:
 
  static int(^maxIntBlock)(int, int) = ^(int a, int b){return a>b?a:b;};
  - (void)fooBar
  {
  int(^maxIntBlockCopied)(int, int) =[maxIntBlock copy];
  }
  void foo()
  {
  int(^maxIntBlockCopied)(int, int) = Block_copy(maxIntBlock);
  }
 
  需要注意的是,这里复制过后的block依旧位于全局区,实际上,复制操作是直接返回了原block对象。
 
  二、block引用的变量在哪里
 
  1.全局区
 
  全局区的变量存储位置与block无关:
 
  static int gVar = 0;
  //__block static int gMVar = 1;
  void foo()
  {
  static int stackVar = 0;
  //    __block static int stackMVar = 0;
  }
 
  注意:static变量是不允许添加__block标记的
 
  2.堆栈
 
iOS开发:Block基础介绍和内存管理
 
  此时,你可能会问,当函数foo返回后,栈上的j已经回收,那么blkInHeap怎么能继续使用它?这是因为没有__block标记的变量,会被当做实参传入block的底层实现函数中,当block中的代码被执行时,j已经不是原来的j了,所谓物是人非就是这样吧~
 
  另外,如果使用到变量j的所有block都没有被复制至heap,那么这个变量j也不会被复制至heap。
 
  因此,即使将j++这一句放到blk()这句之前,这段代码执行后,控制台打印结果也是:1024, 1。而不是1024, 2
 
  三、其他特性
 
  1.复制的行为
 
  对block调用复制,有以下几种情况:
 
  1.对全局区的block调用copy,会返回原指针,并且这期间不处理任何东西(至少目前的内部实现是这样);
 
  2.对栈上的block调用copy,每次会返回新复制到堆上的block的指针,同时,所有__block变量都会被复制至堆一份(多次拷贝,只会生成一份)。
 
  3.对已经位于heap上的block,再次调用copy,只会增加block的引用计数。
 
  为什么我们不讨论retian的行为?原因是并没有Block_retain()这样的函数,而且objc里面的retain消息发送给block对象后,其内部实现是什么都不做。
 
  2.objc类中的block复制
 
  objc类实例方法中的block如果被复制至heap,那么当前实例会被增加引用计数,当这个block被释放时,此实例会被减少引用计数。
 
  但如果这个block没有使用当前实例的任何成员,那么当前实例不会被增加引用计数。这也是很自然的道理,我既然没有用到这个instance的任何东西,那么我干嘛要retian它?
 
  我们要注意的一点是,我看到网上有很多人说block引起了实例与block之间的循环引用(retain-cycle),并且给出解决方案:不直接使用self而先将self赋值给一个临时变量,然后再使用这个临时变量。
 
  但是,大家注意,我们一定要为这个临时变量增加__block标记。

相关文章

相关推荐

最新攻略

用户评论

(已有0条评论)
表情
注:您的评论需要经过审核才能显示哦,请文明发言!
还没有评论,快来抢沙发吧!