Skip to content

Commit da3a821

Browse files
committed
update 2016年 3月30日 星期三 19时44分18秒 CST
1 parent 564f3fb commit da3a821

File tree

1 file changed

+55
-26
lines changed
  • source/iOS/ObjC-Basic

1 file changed

+55
-26
lines changed

source/iOS/ObjC-Basic/MM.md

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -201,25 +201,6 @@ for (int i = 0; i < 100000000; i++)
201201
* 当 block 以异常(exception)结束时,pool 不会被 drain
202202
* Pool 的 drain 操作会把所有标记为 autorelease 的对象的引用计数减一,但是并不意味着这个对象一定会被释放掉,我们可以在 autorelease pool 中手动 retain 对象,以延长它的生命周期(在 MRC 中)。
203203

204-
#### weak 与 Autorelease
205-
206-
众所周知,weak 不会持有对象,当给一个 weak 赋以一个自己生成的对象后,对象会立马被释放。
207-
208-
一个很常见的 warning 就是 Assigning retained object to weak variable, object will be released after assignment.
209-
210-
但是我们前面也提到了,可以持有非自己生成的对象,这通过 autorelease 实现。
211-
212-
那么如果一个 weak 被赋以一个非自己生成的对象呢?代码如下:
213-
214-
```objectivec
215-
__weak NSNumber *number = [NSNumber numberWithInt:100];
216-
NSLog(@"number = %@", number);
217-
```
218-
219-
这种情况下是可以正确打印值的。
220-
221-
[clang的文档](http://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-loadweak) 是这么说的:这种情况下,weak 并不会立即释放,而是会通过 `objc_loadWeak` 这个方法注册到 AutoreleasePool 中,以延长生命周期。
222-
223204
#### main.m 中 Autorelease Pool 的解释
224205

225206
大家都知道在 iOS 程序的 main.m 文件中有类似这样的语句:
@@ -377,6 +358,26 @@ self.property = a;
377358
378359
这个和我们之前在 MRC 中做的不是完全一样。ARC 会把对象的生命周期延长,确保调用者能拿到并且使用这个返回值,但是并不一定会使用 autorelease,文档写的是在 worst case 的情况下才可能会使用,因此调用者不能假设返回值真的就在 autorelease pool 中。从性能的角度,这种做法也是可以理解的。如果我们能够知道一个对象的生命周期最长应该有多长,也就没有必要使用 autorelease 了,直接使用 release 就可以。如果很多对象都使用 autorelease 的话,也会导致整个 pool 在 drain 的时候性能下降。
379360

361+
##### weak 与 Autorelease
362+
363+
众所周知,weak 不会持有对象,当给一个 weak 赋以一个自己生成的对象后,对象会立马被释放。
364+
365+
一个很常见的 warning 就是 Assigning retained object to weak variable, object will be released after assignment.
366+
367+
但是我们前面也提到了,可以持有非自己生成的对象,这通过 autorelease 实现。
368+
369+
那么如果一个 weak 被赋以一个非自己生成的对象(即上面提到的 unretained return value)呢?代码如下:
370+
371+
```objectivec
372+
__weak NSNumber *number = [NSNumber numberWithInt:100];
373+
NSLog(@"number = %@", number);
374+
```
375+
376+
这种情况下是可以正确打印值的。
377+
378+
[clang的文档](http://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-loadweak) 是这么说的:这种情况下,weak 并不会立即释放,而是会通过 `objc_loadWeak` 这个方法注册到 AutoreleasePool 中,以延长生命周期。
379+
380+
380381
##### ARC 下是否还有必要在 dealloc 中把属性置为 nil?
381382
382383
为了解决这个问题,首先让我们理清楚属性是个什么存在。属性(property) 实际上就是一种语法糖,每个属性背后都有实例变量(Ivar)做支持,编译器会帮我们自动生成有关的 setter 和 getter,对于下面的 property:
@@ -452,19 +453,47 @@ myController.completionHandler = ^(NSInteger result) {
452453
}
453454
```
454455

455-
#### ARC 在运行时期的优化
456+
##### ARC 在运行时期的优化
457+
458+
上面提到对于 unretained return value, ARC “并不一定会使用 autorelease”,下面具体解释一下。
456459

457-
ARC 所做的事情并不仅仅局限于在编译期找到合适的位置帮你插入合适的 `release` 等等这样的方法,其在运行时期也做了一些优化,如下是两个优化的例子:
460+
ARC 所做的事情并不仅仅局限于在编译期找到合适的位置帮你插入合适的 `release` 等等这样的内存管理方法,其在运行时期也做了一些优化,如下是两个优化的例子:
458461

459-
- 1.合并对称的引用计数操作。比如将 +1/-1/+1/-1 直接置为 0.
462+
1. 合并对称的引用计数操作。比如将 +1/-1/+1/-1 直接置为 0.
463+
464+
2. 巧妙地跳过某些情况下 `autorelease` 机制的调用。
465+
466+
其中第二个优化,是 ARC 针对 `autorelease` 返回值提供的一套优化策略,大体的流程如下:
467+
468+
当方法全部基于 ARC 实现时,在方法 return 的时候,ARC 会调用 `objc_autoreleaseReturnValue()` 以替代 MRC 下的 `autorelease`。在 MRC 下需要 retain 的位置,ARC 会调用 `objc_retainAutoreleasedReturnValue()`。因此下面的 ARC 代码:
469+
470+
```objectivec
471+
+ (instancetype)createSark {
472+
return [self new];
473+
}
474+
// caller
475+
Sark *sark = [Sark createSark];
476+
```
477+
478+
实际上会被改写成类似这样:
479+
480+
```objectivec
481+
+ (instancetype)createSark {
482+
id tmp = [self new];
483+
return objc_autoreleaseReturnValue(tmp); // 代替我们调用autorelease
484+
}
485+
// caller
486+
id tmp = objc_retainAutoreleasedReturnValue([Sark createSark]) // 代替我们调用retain
487+
Sark *sark = tmp;
488+
objc_storeStrong(&sark, nil); // 相当于代替我们调用了release
489+
```
460490
461-
- 2.巧妙地跳过某些情况下 `autorelease` 机制的调用
491+
有了这个基础,ARC 可以使用一些优化技术。在调用 `objc_autoreleaseReturnValue()` 时,会在栈上查询 return address 以确定 return value 是否会被直接传给 `objc_retainAutoreleasedReturnValue()`。 如果没传,说明返回值不能直接从提供方发送给接收方,这时就会调用 `autorelease`。反之,如果返回值能顺利的从提供方传送给接收方,那么就会直接跳过 `autorelease` 过程,并且修改 return address 以跳过 `objc_retainAutoreleasedReturnValue()`过程,这样就跳过了整个 `autorelease` 和 `retain`的过程
462492
463-
如下用来描述优化 2 的大致的流程:
493+
> 核心思想:当返回值被返回之后,紧接着就需要被 retain 的时候,没有必要进行 autorelease + retain,直接什么都不要做就好了。
464494
465-
当方法全部基于 ARC 实现时,在方法 return 的时候,ARC 会调用 `objc_autoreleaseReturnValue()` 以替代 MRC 下的 `autorelease`。在 MRC 下想要 retain 的时候,ARC 会调用 `objc_retainAutoreleasedReturnValue()`
495+
另外,当函数的调用方是非 ARC 环境时,ARC 还会进行更多的判断,在这里不再详述,详见 [黑幕背后的 Autorelease](http://blog.sunnyxx.com/2014/10/15/behind-autorelease/)
466496
467-
在调用 `objc_autoreleaseReturnValue()` 时,会在栈上查询 return address 以确定 return value 是否会被直接传给 `objc_retainAutoreleasedReturnValue()`。 如果没传,说明返回值不能直接从提供方发送给接收方,这时就会调用 `autorelease`,反之,如果返回值能顺利的从提供方传送给接收方,那么就会直接跳过 `autorelease` 过程,并且修改 return address 以跳过 `objc_retainAutoreleasedReturnValue()`过程,这样就跳过了整个 `autorelease``retain`的过程。
468497
469498
### 关于如何写一个检测循环引用的工具
470499

0 commit comments

Comments
 (0)