博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
blocks在Objective-C中是怎么工作的?
阅读量:4110 次
发布时间:2019-05-25

本文共 2734 字,大约阅读时间需要 9 分钟。

你真的知道blocks在Objective-C中是怎么工作的吗?

来做个小测试看看吧。

所有的例子都以下版本上测试过:

Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn)

Target: x86_64-apple-darwin11.4.2

Thread model: posix

例子1

void exampleB_addBlockToArray(NSMutableArray *array) {

  
char
b =
'B'
;
  
[array addObject:^{
    
printf(
"%c\n"
, b);
  
}];
}
void
exampleB() {
  
NSMutableArray
*array = [
NSMutableArray
array];
  
exampleB_addBlockToArray(array);
  
void
(^block)() = [array objectAtIndex:0];
  
block();
}

A.始终能够正常运行                B.只有在使用ARC的情况下才能正常运行

C.不使用ARC才能正常运行   D.永远无法正常运行

答:A正确
。这个例子可以正常运行。储存exampleA的栈只有在block停止执行之后才会释放,因此,无论此Block由系统分配到栈中还是我们自己手动分配到堆中,它都可以正常执行。

例子2:

void exampleB_addBlockToArray(NSMutableArray *array) {
  char b = 'B';
  [array addObject:^{
    printf("%c\n", b);
  }];
}
  
void exampleB() {
  NSMutableArray *array = [NSMutableArray array];
  exampleB_addBlockToArray(array);
  void(^block)() = [array objectAtIndex:0];
  block();
}

A.始终能够正常运行                B.只有在使用ARC的情况下才能正常运行

C.不使用ARC才能正常运行   D.永远无法正常运行

答:B正确。如果不使用ARC,这个block是一个NSStackBlock,分配给exampleB_addBlockToArray的栈上。而当它在exampleB中执行的时候,由于栈被清空,block不再有效。而使用ARC的话,block会分配到堆中,作为一个自动释放的NSMallocBlock。

例子3

void exampleC_addBlockToArray(NSMutableArray *array) {
  [array addObject:^{
    printf("C\n");
  }];
}
  
void exampleC() {
  NSMutableArray *array = [NSMutableArray array];
  exampleC_addBlockToArray(array);
  void(^block)() = [array objectAtIndex:0];
  block();
}
A.始终能够正常运行                B.只有在使用ARC的情况下才能正常运行

C.不使用ARC才能正常运行   D.永远无法正常运行

答:A正确,由于block在自己的环路中不会抓取任何变量,它不需要在在运行的时候设置state,它会作为一个NSGlobalBlock编译。它既不是栈也不是堆,而是代码片段的一部分。所以它始终都能正常运行。

例子4

typedef void (^dBlock)();
  
dBlock exampleD_getBlock() {
  chard = 'D';
  return^{
    printf("%c\n", d);
  };
}
  
void exampleD() {
  exampleD_getBlock()();
}
A.始终能够正常运行                B.只有在使用ARC的情况下才能正常运行

C.不使用ARC才能正常运行   D.永远无法正常运行

答: B正确。这个例子和例子2类似。如果不使用ARC,block会在exampleD_getBlock的栈上创建起来。然后当功能返回的时候会立即失效。然而,以这个例子来说,这个错误非常明显,所以编译器进行编译会失败,错误提示是:error: returning block that lives on the local stack(错误,返回的block位于本地的栈)。

例子5

typedef void (^eBlock)();
  
eBlock exampleE_getBlock() {
  chare = 'E';
  void(^block)() = ^{
    printf("%c\n", e);
  };
  return block;
}
  
void exampleE() {
  eBlock block = exampleE_getBlock();
  block();
}
A.始终能够正常运行                B.只有在使用ARC的情况下才能正常运行

C.不使用ARC才能正常运行   D.永远无法正常运行

答:B正确。这个例子和例子4类似,除了编译器没有认出有错误,所以代码会进行编译然后崩溃。更糟糕的是,这个例子比较特别,如果你关闭了优化,则可以正常运行。所以在测试的时候需要注意。如果使用ARC的话,block则会正确的位于堆上,作为一个自动释放的NSMAllocBlock。

结论

这套小测试有什么意义呢?意义就是要一直使用ARC。使用ARC,block大部分情况下都可以正常运行。


如果不使用ARC,谨慎起见,可以block = [[block copy] autorelease],这样block会比申明它的栈flame的有效期长。这样block会被作为一个NSMAllocBlock强制复制到堆上。


但是,当然不会这么简单,根据苹果的文档,
Block只有当你在ARC模式下传递block到栈上才会工作,比如说返回的时候。你不需要再次调用Block Copy了。但是当block从栈上传递到 arrayWithObjects: 和其他做了一个retain的方法是时,仍然需要使用[^{} copy]。

但是有一个LLVM的维护者之后也说过:
我们认为这是编译器的bug,它现在已经修复了。但是Xcode以后是否会在以后发布的新版本中解决这个问题,我也不知道。

所以,希望,苹果也把它认为是一个bug,在以后的新版本中会解决这个问题。让我们看看会怎么样吧。

转载地址:http://xcosi.baihongyu.com/

你可能感兴趣的文章
git常用工具的原理介绍以及项目使用中常见问题(持续更新)
查看>>
Java性能监控工具Arthas实践
查看>>
Java虚拟机(JVM)调优和Debug的常用参数详解
查看>>
Java内存溢出的典型场景测试
查看>>
操作系统之进程调度与内存管理
查看>>
分布式系统中的消息队列传递
查看>>
maven如何解决依赖冲突?示例三种bug的解决
查看>>
git的回滚操作
查看>>
记一次新建操作(insert)的优化过程
查看>>
摆桶问题
查看>>
TCP连接延伸7小问
查看>>
增量式PID控制算法的C++代码实现
查看>>
C#计时器timer的嵌套用法
查看>>
C#在word文档中生成多个报告
查看>>
查看linux信息
查看>>
Git: Github提示Key is already use
查看>>
java用法--HashMap putAll
查看>>
java使用--Exception 异常信息 StackTraceElement
查看>>
Hive--对空值和NULL的处理
查看>>
zookeeper错误KeeperErrorCode = ConnectionLoss解决
查看>>