http://www.devdiv.com/arc_-blog-312313-50692.html
arc模式下,在for循环里做nsstring的拼接有什么问题?这是一个面试题
栈溢出
1.ARC基本原则
一个指针指向一个新值,或者超出存在范围-- 可能因为它是局部变量而方法结束了,或者因为它是成员变量而它所属的对象已经被释放--- 所有权才会过期。内存中的对象不再有任何所有者,它的保留计数降至0 从而使该对象被释放。
总结:
从指针角度考虑,指向一块内存中的所有指针都消失了或者都指向了别的对象之后,那么这块内存就被释放掉了。
从内存角度考虑,即这块内存没有任何指针指向了,那么内存被释放
__weakNSString * str = [[NSStringalloc] 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.强指针修饰方式
__strongNSNumber * number = [NSNumbernumberWithBool:YES];
可以使用__strong关键字将变量标识为强指针,但因为变量默认就是强指针,这就有点多余了。
12.默认都是强指针的一个体现
NSMutableArray * array = [[NSMutableArrayalloc] initWithObjects:@"lilei",@"xiaoming",nil];
NSString * name = (NSString *)[arrayobjectAtIndex: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
{
[superdidReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
self.m_name = nil;
if ([self isViewLoaded] && [self.viewwindow] == nil) {
self.view = nil;
}
}
虽然现在是手动管理和ARC过渡阶段,幸运的是你可以将 ARC 和非ARC 代码组合到同一个工程中
@interface CustomView : UIView
{
__weak id <CustomViewDelegate> m_delegate; // 弱指针
NSString * m_buttonTitle;// 强指针
}
@property (nonatomic,weak) __weakid <CustomViewDelegate> m_delegate;
@property (nonatomic,strong) NSString * m_buttonTitle;
非ARC模式下,如下写法
NSMutableArray * array = [[NSMutableArrayalloc] initWithCapacity:0];
self.m_tableDataArray = array;
[array release];
NSMutableArray * array = [[[NSMutableArrayalloc] initWithCapacity:0]autorelease];
self.m_tableDataArray = array;
ARC模式下写法
self.m_tableDataArray = [[NSMutableArrayalloc] initWithCapacity:0];
.m文件里与手动管理一致,只是dealloc里面不能[m_namerelease];了,也不需要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
}
顺便说一下,在你的 dealloc方法中,你仍然可以引用成员变量,因为那时他们还没有被释放,直到dealloc返回。
20.ARC中成员变量和属性
以我的观点,如果使用属性只是 为了简化内存管理的话,现在已经不再必要了。你仍然可以继续这么做,但我认为现在用成员变量更好,而只是你在需要让其他类从公共接口访问到内部数据时才使用属性。
点语法不仅限于属性使用,成员变量也可以使用。
唯一需要访问属性背后的成员变量的地方是在init 中,或者当你提供自定义的 getter和 setter方法时作为最佳时间,如果你定义了一个属性,那么你应该总是使用属性。
•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()函数,来保存内存平衡。
总结一下:
将所有权从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了。
__weakSecondViewController * 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 = [[NextViewControlleralloc] init];
}
return controller;
}
使用GCD库的dispatch_once()方法以确保对象的alloc,init确实只被执行一次,即使同一时刻多个线程去尝试执行这个block都没有问题。
+(id)sharedInstance
{
static NextViewController * controller =nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
controller = [[NextViewControlleralloc] 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结构)管理不善,以及真的是代码没写好