1.ARC基本原则
你还像往常一样编程,只是不能再调用retain,release,autorelease了,这基本上就是ARC的全部。
2.ARC是什么
认识到
ARC
是
objective-c
编译器的一个特性是很重要的,因此与
ARC
相关的一切都发 生在构建你的程序时。
ARC
不是一个运行时特性(有一小部分例外,就是弱指针系统), 它也不是你从其他语言了解的垃圾回收。
ARC实质上还是引用计数模式的内存管理,只不过系统帮你处理了,内存的垃圾回收机制占用系统内存,影响性能。
3.ARC做了什么
如果开启了自动引用计数,编译器就会在你程序中的恰当位置,插入
retain
,
release
和
autorelease
。
ARC
所作的只是在编译代码时向其中插入
retain
和
release
ARC
所作的只是在编译代码时向其中插入
retain
和
release
,就在你会自己添加他们的地 方
--
或者至少是你应该添加他们的地方。这就使
ARC
和手动管理代码一样快速,有时候 甚至更快一些,因为它私下可以执行某些优化。
4.ARC怎么retain对象
OC对象的指针指向一个对象即可,默认情况下,成员变量和局部变量是强指针, 即是某块内存的所有者,且只能是OC对象。
5.ARC怎么release对象
你要做的只是创建一个对象的指针。当指针得到一个新值或者不再 存在,相关联的对象会被释放。这对于所有变量都适用:成员变量,同步属性,甚至是局部 变量。
一个指针指向一个新值,或者超出存在范围 -- 可能因为它是局部变量而方 法结束了,或者因为它是成员变量而它所属的对象已经被释放 --- 所有权才会过期。内存中的对象不再有任何所有者,它的保留计数降至 0 从而使该对象被释放。
总结:
从指针角度考虑,指向一块内存中的所有指针都消失了或者都指向了别的对象之后,那么这块内存就被释放掉了。
从内存角度考虑,即这块内存没有任何指针指向了,那么内存被释放
6.ARC怎么autorelease对象
与第五点相同,只是在合适的延迟时间之后释放。
7.弱指针--归零弱指针
弱指针指向对象,不是该对象的拥有者,所以,该对象释放后,弱指针被自动设置为nil,即所谓的zeroing归零弱指针
8.归零弱指针的好处
注意这是特别便利的特性,因为它防止了弱指针指向已被释放的内存。这类事情过去曾 导致了大量的
BUG --
你可能听说过
"
野指针
"
或者
"
僵尸
" --
但是感谢这些归零弱指针,那 些事不再会发生!
9.
归零弱指针使用场合
你大概不会频繁使用弱指针。他们在两个对象是父子关系时最有用。父母会对孩子拥有 强指针
---
因此
"
拥有
"
孩子
--
但是为了防止所有权循环,孩子仅对父母拥有弱指针。
委托模式是一个典型的例子,即代理模式。
如果使用手动内存管理,一般要在本类的dealloc里面,如果别的类的delegate设置为nil。
10.归零弱指针错误使用方式
__weak NSString * str = [[NSString alloc] initWithFormat:@"%d", 5];
NSLog(@"str = %@", str);
2013-04-28 14:14:55.800 ARC[1635:c07] str = (null)---->归零弱指针的好处
string 对象没有所有者(因为 str 是弱指针),所以对象会在创建后立刻被释放。Xcode 在 你做这件事的时候会给出一个警告因为这可能并非是你所希望发生的事情("Warning: assigning retained object to weak variable; object will be released after assignment")。
记住一点:如果弱指针指向的对象没有所有者,对象会在创建之后立刻被释放。
11.强指针修饰方式
__strong NSNumber * number = [NSNumber numberWithBool:YES];
可以使用__strong 关键字将变量标识为强指针,但因为变量默认就是强指针,这就有点多余了。
12.默认都是强指针的一个体现
NSMutableArray * array = [[NSMutableArray alloc] initWithObjects:@"lilei", @"xiaoming",nil];
NSString * name = (NSString *)[array objectAtIndex:0];
[array removeObjectAtIndex:0];
NSLog(@"lilei name is ----> %@", name);
2013-04-28 14:34:25.610 ARC[1828:c07] lilei name is ----> lilei
13.ARC的一些限制
自动引用计数也有一些限制。作为起步,ARC 只对 objective-c 有效。如果你的程序使 用 core fundation 或者malloc()和 free(),那么你仍然对其内存管理负有责任。而且,某些语言规则会更加严格以确保 ARC 总能正确工作。这些仅仅是 小的牺牲,你获得的好处将比你放弃的要多得多!
仅仅是因为 ARC 为你在恰当的位置处理 retain 和 release,并不意味着你可以完全忘掉 内存管理。因为强指针使对象保持存在,还是会有些情况下需要你手动将这些指针置为 nil,否则程序将用光其内存。如果你保留所有你曾经创建的对象,那么 ARC 也就永远不能 释放他们。所以,不论你何时创建对象,你都还是需要考虑谁拥有它,以及此对象应该存在 多久。 比如发生内存警告时的处理,如下,虽然不可以调用release,retain,autorelease,但是,点语法还是可以使用的。
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
self.m_name = nil;
if ([self isViewLoaded] && [self.view window] == nil) {
self.view = nil;
}
}
虽然现在是手动管理和ARC过渡阶段,幸运的是你可以将 ARC 和非 ARC 代码组合到 同一个工程中
而且你还可以指定一个类是否使用ARC模式
14.ARC模式下属性的声明方法
@interface CustomView : UIView
{
__weak id <CustomViewDelegate> m_delegate; // 弱指针
NSString * m_buttonTitle; // 强指针
}
@property (nonatomic, weak) __weak id <CustomViewDelegate> m_delegate;
@property (nonatomic, strong) NSString * m_buttonTitle;
非ARC模式下,如下写法
NSMutableArray * array = [[NSMutableArray alloc] initWithCapacity:0];
self.m_tableDataArray = array;
[array release];
NSMutableArray * array = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
self.m_tableDataArray = array;
ARC模式下写法
self.m_tableDataArray = [[NSMutableArray alloc] initWithCapacity:0];
.m文件里与手动管理一致,只是dealloc里面不能[m_name release];了,也不需要self.m_name = nil;了,OC强指针的对象不需要dealloc函数了,
15.ARC模式下的dealloc
你仍然可以实现 dealloc, 但是,dealloc里面,你被禁止使用 release 和[super dealloc]。在大多数情况下,你都不必写 dealloc 方法。当一个对象被 释放时,它的实例变量和同步属性自动被释放,不再需要你手动释放成员变量了,dealloc里面需要释放定时器和通知,ARC不不负责这两个内容。
16.ARC中的switch case语句
如果你在一个case中生命一个新的指针变量,你就必须将真个case包含在一个花括号中,这样变量的作用域就非常明确,这正是ARC需要知道的,这样ARC才能在正确的时刻施放对象,总之,最好一个case一个{},看起来干净,用起来作用域明显挺好的事。
17.ARC中的autoreleasepool
autoreleasepool是一种结构,不再是OC对象,作用不变,写法如下
@autoreleasepool {
// do something
}
作用域与普通{}一样。
18.ARC限制之一
你不再能够将 Objective-C 对象放到 C 结构中了
19.ARC模式下保留dealloc的唯一情况
当你需要释放某些不在
ARC
保护伞之下的资源时。比 如说对
Core Fundation
对象调用
CFRelease(),对使用 malloc()分配的内存调用 free(),反注
册通知,停止记时器,等等。
顺便说一下,在你的 dealloc 方法中,你仍然可以引用成员变量,因为那时他们还没有 被释放,直到 dealloc 返回。
20.ARC中成员变量和属性
以我的观点,如果使用属性只是 为了简化内存管理的话,现在已经不再必要了。你仍然可以继续这么做,但我认为现在用成员变量更好,而只是你在需要让其他类从公共接口访问到内部数据时才使用属性。
点语法不仅限于属性使用,成员变量也可以使用。
唯一需要访问属性背后 的成员变量的地方是在 init 中,或者当你提供自定义的 getter 和 setter 方法时
作为最佳时间,如果你定义了一个属性,那么你应该总是使用属性。
21.ARC中的属性修饰语
• strong. 是 retain 的同义词。一个强属性会成为所指向对象的所有者。
• weak. 这个属性代表一个弱指针。当所指向的对象被释放时,他会自动被设为 nil。记 住,对于 outlet 使用它。
• unsafe_unretained.这是原来的"assign"的同义词。它只在特殊情况下以及你想将目标设 为 iOS4 时使用。后面会讲到它。
你必须显式的声明你希望此属性为 strong, weak 或者 unsafe_unretained。大多数情况下 strong 是恰当的答案:
• copy. 这还是和以前一样。这将制作对象的一份拷贝,并创建强关系。
• assign. 你能再为对象使用它了,但你还是可以用于基础类型如 BOOL, int 和 float。
22.core foundation对象和Object-C对象所有权之间的转换
现在我们有了 ARC,编译器需要知道谁负责释放那些转换的对象。如果你将 NSObject 作为 Core Fundation对象,那么 ARC 不会负责释放它。但你确实需要告诉 ARC 你的意图, 编译器不能自己来推断。同样的,如果你创建了一个 Core Fundation 对象但将其转换为了 NSObject 对象,你就需要告诉 ARC 得到它的所有权,并及时释放它。这就是桥接转换要做 的。
• __bridge_transfer: 给予 ARC 所有权,从core foundation到object-c
有一个名为 CFBridgingRelease()的帮助函 数。它做了和__bridge_trasfer 转换同样的事情
• __bridge_retained: 解除 ARC 的所有权 ,从object-c到core foundation
有一个名为CFBridgingRetain()的帮助函数,它做了和__bridge_retained相同的事情,它让core foundation保存对象。 这个时候要调用CFRelease()函数,来保存内存平衡。
注意:
不是所有的 Objective-c 和 Core Fundation 对象可以免费桥接的。比如,CGImage 和 UIImage 就不能彼此转换,CGColor 和 UIColor 也不行。下面的网页列出了可以互相转换 的类型:http://bit.ly/j65Ceo
总结一下:
将所有权从core foundation转移到object-c时,需要使用CFBridgingRelease().
将所有权从object-c转移到core foundation时,需要使用CFBridgingRetain().
当你想将一种类型临时当作另一种类型使用,而不转移所有权时,需要使用__bridge.
23. ARC中的内存泄露
环形持有一:timer--->view<----controller
timer<---view
1 2 1
当controller释放之后,controller的拥有者为0,执行其dealloc
环形持有一:timer--->view
timer<---view
1 1
在controller的dealloc里面调用 timer invalidate后,达到释放目的
环形持有一:timer--->view
timer<---view
0 0
如果一个自定义的view类,里面声明一个强指针的timer,那么timer和这个view类相互持有,这时,timer和view的拥有者都为1,如果你再把view添加到controller上面,那么view的拥有者为2,当controller执行dealloc后,view的拥有者由2减为1,这个时候不能只能在controller里面调用方法使view里面的定时器停止,打破循环持有的状态。
链状持有一:runloop---->timer---->view-------->源头得不到释放
1 1 1
如果你为了避免上述情况的产生,而对timer使用弱指针,那么情况可能更糟糕。虽然你的类不再拥有timer
但是timer仍然由另一个对象拥有,也就是run loop,所以,也就会导致我们使用的这个类一直被timer拥有,但.(括号中是我自己的理解,因为timer在ARC模式下有且仅有一个持有者,如果自己的类不持有timer,那么只能交给run loop了,但是,run loop持有timer的话,我们自己不能释放timer,自己的类可以释放timer,所以,timer一定要使用强指针)
环形持有二:controller--->customview--->block
1 1 1
如果在controller里面调用customview的block,且block里面直接调用self,self就是controller或者block捕获持有controller的变量,这样block间接地持有controller,下面的循环里只是多一个timer而已
环形持有二:controller--->customview--->block
<----block持有controller----
2 1 1
那么当controller被释放时,其拥有者由2减为1,其dealloc不执行
环形持有二:controller--->customview--->block
1 1 1
解决方法:一种方案是在 block 内部不要使用 self。,避免形成环状持有的状态,不让customview里面的block持有controller,也就是不能在block里面直接或间接地出现self,那意味着你不能 调用任何属性,实例变量,或者来自 block 的方法。局部变量可以。你不能使用实例变量的 原因是在场景后面会做 self-ivar 处理,因此仍然调用了 self。你使用待*的和id类型或者基本类型int BOOL等的实例变量时,xcode会给出形成循环持有的提示"Capturing 'self' strongly in this block is likely to lead to a retain cycle"
个人验证了一下,普通的block,比如一个controller类里的block不会出现这样的情况。
常用模型:
一、属性,局部变量,或者来自block的方法,都是用局部变量转换一下,完全避免self被调用,这个显然不太好
二、self局部化,如下所示,这样就可以随便使用属性了,因为self没有被block捕获而使得引用计数加一。但是,成员变量还是会形成,所有权循环,要么使用局部变量转换一下,要么都声明为属性,但是,前面文章提到如果不是外部借口需要使用,而仅仅是内存管理方面没有没有必要都使用属性。不过也可以成员变量在block使用时,用局部变量转换一下就OK了。
__weak SecondViewController * weakSelf = self;
m_view.m_block = ^ {
SecondViewController * strongSelf = weakSelf;
if (strongSelf != nil) {
NSLog(@"%@", strongSelf.m_testString);
}
};
只有像上面所示的block类型会出现问题,类自己内部的block不会出现此现象。block限定词最好是copy,strong有时候可能会崩溃。
在MRR模式下,上面类型的block这么写就OK了,任何以__block关键字为前缀的变量都不能被这个block保留引用计数。如果不加__block关键字,任何在block内部用到的指针变量,block都会copy一个只读复制的值,如果加了__block关键字,不复制值了,而且变量变为可读可写的了。
__block SecondViewController * weakSelf = self;
m_view.m_block = ^ {
};
保持对象的活动状态
__block DelayedOperation *operation = [[DelayedOperation alloc] initWithDelay:5 block:^
{
NSLog( @"Performing operation" ); // do stuff
operation = nil ; }];
24.singleton
一帮情况下写法,但是如果你的单利用于多线程那么下面这个方法还不够健全。
+(id)sharedInstance
{
static NextViewController * controller = nil;
if (controller == nil) {
controller = [[NextViewController alloc] init];
}
return controller;
}
使用GCD库的dispatch_once()方法以确保对象的alloc,init确实只被执行一次,即使同一时刻多个线程去尝试执行这个block都没有问题。
+(id)sharedInstance
{
static NextViewController * controller = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
controller = [[NextViewController alloc] init];
});
return controller;
}
25.ARC模式下变量的默认初始化
ARC模式下,所有指针变量默认是nil,基本类型默认包含一些垃圾值,所以要赋具体的数值,否则会出现编译警告。这样使用一个没有指向一个有效对象的指针几乎不可能。
非ARC模式下,只有属性,成员变量的指针变量默认才是nil,局部指针变量不是nil,基本类型是随机数,而且会有编译警告,所以,要初始化为具体的值。
25.ARC中的内存泄露--转载网络
2,死循环
如果某个ViewController中有无限循环,也会导致即使ViewController对应的view关掉了,ViewController也不能被释放。
这种问题常发生于animation处理。
例,
比如,
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
tansition.repeatCount = HUGE_VALL;
[self.view.layer addAnimation:transition forKey:"myAnimation"];
上例中,animation重复次数设成HUGE_VALL,一个很大的数值,基本上等于无限循环了。
解决办法是,在ViewController关掉的时候,停止这个animation。
-(void)viewWillDisappear:(BOOL)animated {
[self.view.layer removeAllAnimations];
}
26
值得注意的是,ARC并不能避免所有的内存泄露。使用ARC之后,工程中可能还会有内存泄露,不过引起这些内存泄露的主要原因是:block,retain循环,对CoreFoundation对象(通常是C结构)管理不善,以及真的是代码没写好。