原文:http://www.2cto.com/kf/201503/385489.html
-
自动释放池
- 在 mrc 的代码中,没有 weak,只有 assign
- assign 修饰符号,对对象不做任何操作,只是简单的记录地址
- weak 是 ARC 专有的,如果对象没有其他任何对象做强引用,会被立即释放!
- weak 的效率非常差!
- assign 会记录住地址,对象释放后,地址仍然保留,在 MRC 开发中,野指针错误非常频繁
-
weak 安全性很好!一旦没有强引用,自动将地址设置为 nil,OC中可以向 nil 发送任何消息都不会抱错!
-
autorelease 作用就是
延迟释放
,给对象添加延迟释放的标记自动释放对象 atuorelease
自动释放对象
- 所有 "autorelease" 的对象,在出了作用域之后,会被自动添加到"最近创建的"自动释放池中!
- 在自动释放池被销毁或者耗尽的时候,会向池中所有对象发送 release 消息,释放池中对象
- 自动释放池,在 ARC & MRC 程序中,同样有效!
MRC 下系统变量的测试
声明变量
12<code>
@property
(nonatomic, assign) UIButton *btn;
</code>
向控制器View中添加按钮:
123456789101112131415161718<code>
// alloc / init - 应该出了作用域才会被释放
//
self.btn = [[UIButton alloc] init];
self.btn.frame = CGRectMake(
40
,
40
,
100
,
100
);
self.btn.backgroundColor = [UIColor orangeColor];
[self.view addSubview:self.btn];
NSLog(@
"%@"
, self.view.subviews);
// 类方法创建的 - 是 ‘autorelease’ 的
// 出了作用域,会被添加到自动释放池中
// 自动释放池被销毁前,才释放对象!
self.btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
[self.view addSubview:self.btn];
NSLog(@
"%@"
, self.view.subviews);
// ARC 上述演练不好使!以 weak 为主!
</code>
运行结果:
123456789101112<code>
2015
-
03
-
25
10
:
31
:
20.055
09
-自动释放池[
27279
:
718327
] (
"<_UILayoutGuide: 0x7fc5d0d346b0; frame = (0 0; 0 0); hidden = YES; layer = <calayer: 0x7fc5d0d2d810="
">>"
,
"<_UILayoutGuide: 0x7fc5d0e77950; frame = (0 0; 0 0); hidden = YES; layer = <calayer: 0x7fc5d0e77790="
">>"
,
"<uibutton: 0x7fc5d0e07910="
" 100="
" frame="
(
40
" layer="
<CALayer:
" opaque="
NO;
">>"
)
2015
-
03
-
25
10
:
31
:
20.063
09
-自动释放池[
27279
:
718327
] (
"<_UILayoutGuide: 0x7fc5d0d346b0; frame = (0 0; 0 0); hidden = YES; layer = <calayer: 0x7fc5d0d2d810="
">>"
,
"<_UILayoutGuide: 0x7fc5d0e77950; frame = (0 0; 0 0); hidden = YES; layer = <calayer: 0x7fc5d0e77790="
">>"
,
"<uibutton: 0x7fc5d0e07910="
" 100="
" frame="
(
40
" layer="
<CALayer:
" opaque="
NO;
">>"
,
"<uibutton: 0x7fc5d0f2f830="
" 22="
" frame="
(
0
" layer="
<CALayer:
" opaque="
NO;
">>"
)
</uibutton:></uibutton:></calayer:></calayer:></uibutton:></calayer:></calayer:></code>
可以看到,无论是alloc还是buttonWithType: 都是可以添加到控制器的View上的,也就意味着,创建出来的button并没有立刻销毁
- assign 声明的变量,在alloc后,对象并不会立刻释放,除了作用域才会销毁,即使是对象销毁后,assign记录的地址并不会清除,这就很容易造成
野指针
错误 - 利用buttonWithType: 创建出来的,也不会立刻释放,系统类方法创建出来的对象,是默认
autorealease
的,出了作用域,会自定添加到自动释放池
中,自动释放池销毁前,才会释放对象,同样assign声明的变量会持有该对象的地址,一直不肯放~MRC下自定义对象的自动释放
Person.h
1234567<code>
@interface
Person : NSObject
+ (instancetype)person;
@end
</code>
Person.m
- 注意:这里没有添加autorelease
1234567891011121314151617
<code>
@implementation
Person
+ (instancetype)person {
// autorelease 作用就是延迟释放
// 给对象添加延迟释放的标记
Person *p = [[Person alloc] init] ;
return
p;
}
- (
void
)dealloc {
NSLog(@
"我去了"
);
[
super
dealloc];
}
@end
</code>
声明Persion变量:
12<code>
@property
(nonatomic, assign) Person *p;
</code>
测试Demo:
12345678<code>- (
void
)viewDidLoad {
[
super
viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.p = [Person person];
NSLog(@
"%@"
, self.p);
}
</code>
运行结果:
12<code>
2015
-
03
-
25
10
:
44
:
16.652
10
-ARC自动释放[
27546
:
726446
] <person:
0x7fb74b45c770
=
""
>
</person:></code>
小结:
在MRC下,如果提供的类方法没有autorelease,那么在调用的时候,用assign变量接收,仍然会打印出对象的地址,当然对象并没有销毁(
dealloc没有输出
)。将类方法带上 autorelease
12<code>Person *p = [[[Person alloc] init] autorelease];
</code>
打印结果:
123<code>
2015
-
03
-
25
10
:
56
:
04.224
10
-ARC自动释放[
27690
:
732059
] <person:
0x7f8c48f23d90
=
""
>
2015
-
03
-
25
10
:
56
:
04.238
10
-ARC自动释放[
27690
:
732059
] 我去了
</person:></code>
可以看到对象被释放了,说明autorelease给对象添加了
延迟释放
的标记,让对象在自动是释放池销毁,或者出了离它最近的自动释放池作用域的时候,自动释放。ARC中的自动释放
变量的声明:
1234<code>
@property
(nonatomic, weak) Person *p;
@property
(nonatomic, weak) UIButton *button;
</code>
测试Demo:
1234567891011121314<code>
// ARC 中,如果给 weak 做 alloc / init 的设置数值,Xcode 会提示!
self.p = [[Person alloc] init];
// 对于 Xcode 编译器而言,只要是类方法,就不会提示!
// 如果使用 MRC 开发,所有 返回 instancetype 的类方法,返回的对象都是 autorelease 的!
// 因为没有强引用,会被立即释放
self.p = [Person person];
// ARC中之所以为 null,是因为 weak,跟autorelease没有关系!
NSLog(@
"%@"
, self.p);
// 之所以会这样是因为苹果框架的底层是用 MRC 写的,buttonWithType返回的对象是带有 autorelease
self.button = [UIButton buttonWithType:UIButtonTypeContactAdd];
NSLog(@
"%@"
, self.button);
</code>
打印结果:
123<code>
2015
-
03
-
25
11
:
00
:
58.756
10
-ARC自动释放[
27778
:
735123
] (
null
)
2015
-
03
-
25
11
:
00
:
58.769
10
-ARC自动释放[
27778
:
735123
] <uibutton:
0x7f96e97668a0
=
""
22
=
""
frame=
"(0"
layer=
"<CALayer:"
opaque=
"NO;"
>>
</uibutton:></code>
小结
- 如果普通对象的alloc,且对象是weak的,Xcode会自动提示警告,告诉你这个对象,一创建就会被销毁啦
- 但是如果你提供了一个类方法,快速生成。即使内部写的是alloc,Xcode也不会警告你,但是你不能改变一创建就被销毁的事实。
- 苹果自己提供的类方法创建的button,我们用weak接收,却没有销毁,为啥呢,因为苹果框架的底层是用MRC写的,buttoWithType:返回的对象都是带
autorelease
的,意味着会延迟销毁,仅仅是被添加到自动释放池而已,等待着出自动释放池作用域或者自动释放池销毁。而不会立刻销毁。一道经典面试题
问题:以下代码是否有问题?如果有,如何解决?
12345678<code>
for
(
long
i =
0
; i < largeNumber; ++i) {
@autoreleasepool
{
NSString *str = [NSString stringWithFormat:@
"hello - %ld"
, i];
str = [str uppercaseString];
str = [str stringByAppendingString:@
" - world"
];
}
}
</code>
答案:如果 largeNumber 非常大,会创建太多自动释放的对象,有可能会把自动释放池"撑满" 提示:经常一次用户交互,远远不止一个 for,在 for 的前后,会有很多的代码,但是这个 for 会占用大量的自动释放池空间
解决方案
引入自动释放池,网络上有两种解决方案!
123456789<code>
1
> 外面加自动释放池(快):能够保证
for
循环结束后,内部产生的自动释放对象,都会被销毁
需要等到
for
结束后,才会释放内存
2
> 里面加自动释放池(慢):能够每一次
for
都释放产生的自动释放对象!
提问:那种方式效率高!速度差不多!大多数测试内部比外部外!
如果面试中问道:要说里面的略快,“亲测”!!!
提示:在日常工作中!有的时候,真的会出现代码内存飙升的情况!要知道解决办法!
</code>
设置largeNumner的数值:
12<code>
long
largeNumber =
5
*
1000
*
1000
;
</code>
测试Demo:
123456789101112131415<code>
NSLog(@
"内部 start"
);
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
for
(
long
i =
0
; i < largeNumber; ++i) {
@autoreleasepool
{
NSString *str = [NSString stringWithFormat:@
"hello - %ld"
, i];
str = [str uppercaseString];
str = [str stringByAppendingString:@
" - world"
];
}
}
NSLog(@
"end - %f"
, CFAbsoluteTimeGetCurrent() - start);
[self answer1];
</code>
answer1方法:
123456789101112<code> NSLog(@
"外面 start"
);
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
@autoreleasepool
{
for
(
long
i =
0
; i < largeNumber; ++i) {
NSString *str = [NSString stringWithFormat:@
"hello - %ld"
, i];
str = [str uppercaseString];
str = [str stringByAppendingString:@
" - world"
];
}
}
NSLog(@
"end - %f"
, CFAbsoluteTimeGetCurrent() - start);
</code>
测试结果:
12345<code>
2015
-
03
-
25
11
:
18
:
50.663
12
-自动释放池的面试题[
28015
:
743360
] 内部 start
2015
-
03
-
25
11
:
19
:
03.996
12
-自动释放池的面试题[
28015
:
743360
] end -
13.332371
2015
-
03
-
25
11
:
19
:
03.996
12
-自动释放池的面试题[
28015
:
743360
] 外面 start
2015
-
03
-
25
11
:
19
:
18.227
12
-自动释放池的面试题[
28015
:
743360
] end -
14.230752
</code>
由测试结果可以知道,对于500w次循环,内部、外部添加autorelease,速度其实差不多,内部略微快一点,但这是取决于编译器。 但是,在外部添加autorelease会占用极大的内存(注意,这里跟不加autorelease不一样,不加autorelease开辟的内存是不会释放的)。 开发中,通常在内部添加。
- 注意:这里没有添加autorelease
- assign 声明的变量,在alloc后,对象并不会立刻释放,除了作用域才会销毁,即使是对象销毁后,assign记录的地址并不会清除,这就很容易造成