<<Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法>>笔记-块与大中枢派发

37、理解“块”这一概念

  “块”是一种可在C、C++ 及 Objective-C 代码中使用的“词法闭包”,它极为有用,这主要是因为借由此机制,开发者可将代码像对象一样传递,令其在不同环境下运行。还有个关键的地方是,在定义“快”的范围内,它可以访问到中的全部变量。

  下面是一个最简单的块:

  ^{

    //Block implementation here

  }

  块其实就是个值,而且自有其相关类型。与 int、float 或 Objective-C 对象一样,也可以把块赋给变量,然后像使用其他变量那样使用它。块类型的语法与函数指针近似。

  默认情况下,为块所捕获的变量,是不可以在块里修改的。声明变量的时候可以加上 __block 修饰符,这样就可以在块内修改了。

  如果将块定义在 Objective-C 类的实例方法中,可以访问类的所以实例变量和self变量。块总能修改实例变量,所以在声明时无须加 __block。

  self 也是个对象,因而块在捕获它时也会将其保留,如果 self 所指代的那个对象同时也保留了块,那么这种情况通常会导致“保留环”。

  定义块的时候,其所占的内存区域是分配在栈中的,这就是说,块只在定义它的范围内有效,例如,下面这段代码就有危险:

  void (^block)();

  if (/* some condition */ ) {

    block = ^{

      NSLog(@"Block A");

    };

  } else {

    block = ^{

      NSLog(@"Block A");

    };

  }

  block();

  定义在if及else语句中的两个块都分配在栈内存中.编译器会给每个块分配好栈内存,然而等离开了相应的范围之后,编译器有可能把分配给块的内存覆写掉,于是,这两个块只能保证在对应的if或else语句范围内有效.这样写出来的代码可以编译,但运行起来时而正确,时而错误.

  为解决此问题,可给块对象发送copy消息以拷贝之.这样的话,就可以把块从栈复制到堆了.拷贝后的块,可以在定义它的那个范围之外使用.

  明白这点后,我们只需给代码加上两个copy方法调用,就可以令其变的安全了:

  void (^block)();

  if (/* some condition */ ) {

    block = [^{

      NSLog(@"Block A");

    } copy];

  } else {

    block = [^{

      NSLog(@"Block A");

    } copy];

  }

  block();

38、为常用的块类型创建 typedef

  以 typedef 重新定义块类型,可令块变量用起来更加简单.

  定义新类型时应遵从现有的命名习惯,勿使其名称与别的类型相冲突.

  不妨为同一个块签名定义多个类型别名.如果要重构的代码使用了块类型的某个别名,那么只需修改相应 typedef 中的块签名即可,无需改动其他 typedef.

39、用 handler 块降低代码分散程度

  在创建对象时,可以使用内联的 handler 块将相关业务逻辑一并声明.

  在有多个实例需要监控时,如果采用委托模式,那么经常需要根据传入的对象来切换,而若改用 handler 块来实现,则可直接将块与相关对象放在一起.

  设计 API 时如果用到了 handler 块,那么可以增加一个参数,使调用者可以通过此参数来决定应该把块安排在哪个队列上执行.

40、用块引用其所属对象时不要出现保留环

  如果块所捕获的对象直接或间接地保留了块本身,那么就得担心保留环问题.

  一定要找个适当的时机解除保留环,而不能把责任推给 API 的调用者.

 

posted @ 2017-08-04 10:53  晚安早安  阅读(140)  评论(0编辑  收藏  举报