ARC - strong和weak指针

 

ARC指南1 - strong和weak指针

 

 

提示:本文中所说的"实例变量"即是"成员变量","局部变量"即是"本地变量"

 

一、简介

ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain、release、autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切

注意:ARC 是编译器特性,而不是 iOS 运行时特性(除了weak指针系统),它也不是垃圾收集器。因此 ARC 和手动内存管理性能是一样的,有时还能更加快速,因为编译器还可以执行某些优化

 

二、原理

ARC 的规则非常简单:只要有变量指向对象,对象就会存在内存。当指针指向新值,或者指针不再存在时,相关联的对象就会自动释放。这条规则对于实例变量、synthesize属性、局部变量都是适用的

 

三、strong指针

控制器中有个文本输入框框属性

@property (nonatomic, assign) IBOutlet UITextField *nameField;  

 1.如果用户在文本框中输入mj这个字符串

那么就可以说,nameField的text属性是NSString对象的指针,也就是拥有者,该对象保存了文本输入框的内容

 

2.如果执行了如下代码

  1. NSString *name = self.nameField.text;  
一个对象可以有多个拥有者,在上面代码中,name变量同样也是这个NSString对象的拥有者,也就是有两个指针指向同一个对象

 

3.随后用户改变了输入框的内容,比如

此时nameFeild的text属性就指向了新的NSString对象。但原来的NSString对象仍然还有一个所有者(name变量),因此会继续保留在内存中

 

4.当name变量获得新值,或者不再存在时(如局部变量方法返回时、实例变量对象释放时),原先的NSString对象就不再拥有任何所有者,retain计数降为0,这时对象会被释放

如,给name变量赋予一个新值

  1. name = @"Jake";  

 

 

我们称name和nameField.text指针为"Strong指针",因为它们能够保持对象的生命。默认所有实例变量和局部变量都是Strong指针

 

四、weak指针

weak型的指针变量仍然可以指向一个对象,但不属于对象的拥有者

1.执行下面的代码

__weak NSString *name = self.nameField.text;  


 

name变量和nameField.text属性都指向同一个NSString对象,但name不是拥有者

 

2.如果文本框的内容发生变化,则原先的NSString对象就没有拥有者,会被释放,此时name变量会自动变成nil,称为空指针

weak型的指针变量自动变为nil是非常方便的,这样阻止了weak指针继续指向已释放对象,避免了野指针的产生,不然会导致非常难于寻找的Bug,空指针消除了类似的问题

 

3.weak指针主要用于“父-子”关系,父亲拥有一个儿子的strong指针,因此父亲是儿子的所有者;但为了阻止所有权循环,儿子需要使用weak指针指向父亲。典型例子是delegate模式,你的ViewController通过strong指针(self.view)拥有一个UITableView, UITableView的dataSource和delegate都是weak指针,指向你的ViewController

 

五、strong和weak指针的使用注意

1.下面代码是有问题的:

 

[java] view plaincopy
  1. __weak NSString *str = [[NSString alloc] initWithFormat:@"1234"];  
  2. NSLog(@"%@", str); // 打印出来是"(null)"  
str是个weak指针,所以NSString对象没有拥有者,在创建之后就会被立即释放。Xcode还会给出警告("Warning: Assigning retained object to weak variable; object will be released after assignment")

 

 

2.一般的指针变量默认就是strong类型的,因此一般我们对于strong变量不加__strong修饰,以下两行代码是等价的:

[java] view plaincopy
  1. NSString *name = self.nameField.text;  
  2. __strong NSString *name = self.nameField.text;  

 

 

3.属性可以是strong或weak,写法如下

 

[java] view plaincopy
  1. @property (nonatomic, strong) NSString *name;  
  2. @property (nonatomic, weak) id delegate;  

4.以下代码在ARC之前是可能会行不通的,因为在手动内存管理中,从NSArray中移除一个对象时,这个对象会发送一条release消息,可能会被立即释放。随后NSLog()打印该对象就会导致应用崩溃

 

 

[java] view plaincopy
  1. id obj = [array objectAtIndex:0];  
  2. [array removeObjectAtIndex:0];  
  3. NSLog(@"%@", obj);  
在ARC中这段代码是完全合法的,因为obj变量是一个strong指针,它成为了对象的拥有者,从NSArray中移除该对象也不会导致对象被释放

 

 

六、ARC小结

1.有了ARC,我们的代码可以清晰很多,你不再需要考虑什么时候retain或release对象。唯一需要考虑的是对象之间的关联,也就是哪个对象拥有哪个对象?

2.ARC也有一些限制:

1> 首先ARC只能工作于Objective-C对象,如果应用使用了Core Foundation或malloc()/free(),此时还是需要你来手动管理内存

2> 此外ARC还有其它一些更为严格的语言规则,以确保ARC能够正常地工作

3.虽然ARC管理了retain和release,但并不表示你完全不需要关心内存管理的问题。因为strong指针会保持对象的生命,某些情况下你仍然需要手动设置这些指针为nil,否则可能导致应用内存不足。无论何时你创建一个新对象时,都需要考虑谁拥有该对象,以及这个对象需要存活多久

4.ARC还能很好地结合C++使用,这对游戏开发是非常有帮助的。对于iOS 4,ARC有一点点限制(不支持weak指针),但也没太大关系

 

七、ARC使用注意总结

 

1.不能直接调用dealloc方法,不能调用retain,release,autorelease,retainCount方法,包括@selector(retain)的方式也不行
2.可以用dealloc方法来管理一些资源,但不能用来释放实例变量,也不能在dealloc方法里面去掉[super dealloc]方法,在ARC下父类的dealloc同样由编译器来自动完成
3.Core Foundation类型的对象仍然可以用CFRetain,CFRelease这些方法
4.不能再使用NSAllocateObject和NSDeallocateObject对象
5.不能在C结构体中使用对象指针,如果有类似功能可以创建一个Objective-C类来管理这些对象
6.在id和void*之间没有简便的转换方法,同样在Objective-C和Core Foundation类型之间的转换都需要使用编译器制定的转换函数
7.不能再使用NSAutoreleasePool对象,ARC提供了@autoreleasepool块来代替它,这样更有效率
8.不能使用内存存储区(不能再使用NSZone)
9.不能以new为开头给一个属性命名
10.声明IBOutlet时一般应当使用weak,除了对StoryBoard这样nib中间的顶层对象要用strong
11.weak相当于老版本的assign,strong相当于retain

 

 

ARC指南2 - ARC的开启和禁止

分类: iOS基础

要想将非ARC的代码转换为ARC的代码,大概有2种方式:

1.使用Xcode的自动转换工具

2.手动设置某些文件支持ARC

 

一、Xcode的自动转换工具

Xcode带了一个自动转换工具,可以将旧的源代码转成ARC模式

1.ARC是LLVM 3.0编译器的特性,而现有工程可能使用老的GCC 4.2或LLVM-GCC编译器,因此首先需要设置使用LLVM 3.0编译器:

(现使用的XCode4.5,LLVM 3.0已经升级到LLVM 4.1)

最好也选上Warnings中的Other Warning Flags 为 -Wall,这样编译器就会检查所有可能的警告,有助于我们避免潜在的问题

 

2.Build Options下面的Run Static Analyzer选项也最好启用,这样每次Xcode编译项目时,都会运行静态代码分析工具来检查我们的代码

 

3.设置"Objective-C Automatic Reference Counting"选项为YES,不过Xcode自动转换工具会自动设置这个选项,这里只是说明一下如何手动设置

 

4.打开Xcode的自动转换工具

 

5.Xcode会显示一个新窗口,让你选择哪些文件需要转换

点击Check按钮,Xcode可能会弹出对话框提示项目不能转换为ARC,需要你准备好转换(这里暂时省略详细说明)

 

6.如果没有什么警告、错误了,就会弹出一下提示窗口:

 

7.点击Next,几秒钟后,Xcode会提示所有文件的转换预览,显示源文件的所有改变。左边是修改后的文件,右边是原始文件。在这里你可以一个文件一个文件地查看Xcode的修改,以确保Xcode没有改错你的源文件:

点击Save即可完成转换

 

8.自动转换之后,Xcode会移除所有retain、release、autorelease调用,这可能会导致代码出现其它警告、无效语法等,这些都需要自己手工进行修改

注意:Xcode的自动转换工具最好只使用一次,多次使用可能会出现比较诡异的问题。假如你第一次转换没有转换所有的文件,当你稍后试图再次转换剩余的文件时,Xcode实际上不会执行任何转换操作。因此最好一次就完成转换,没有转换的文件可以考虑手工进行修改

 

二、手动开启某些文件的ARC

在Compiler Flags一列加上-fobjc-arc就表示开启这个.m文件的ARC

 

三、禁止某些文件的ARC

在Compiler Flags一列加上-fno-objc-arc就表示禁止这个.m文件的ARC

 

 

 

ARC指南3 - @property

分类: iOS基础

本章介绍引入ARC后@property的使用,跟ARC之前的还是很不一样的

 

一、.h和.m文件的变化说明

1.对于.h头文件,主要是将属性定义由retain变为strong

 

[java] view plaincopy
  1. @property (retain, nonatomic)  
变为

 

[java] view plaincopy
  1. @property (strong, nonatomic)  

2.在ARC之前,我们经常在.m中使用分类拓展来增加私有的property

 

 

[java] view plaincopy
  1. @interface MJViewController ()  
  2. @property (nonatomic, retain) NSArray *data;  
  3. @end  
这样做主要是简化实例对象的手动内存管理,让property的setter方法自动管理原来对象的释放,以及新对象的retain。但是有了ARC,这样的代码就不再需要了。一般来说,仅仅为了简化内存管理,是不再需要使用property的,虽然你仍然可以这样做,但直接使用实例变量是更好的选择。只有那些属于public的实例变量,才应该定义为property

 

我们可以直接在.m类实现中定义private实例变量,不必写分类拓展了:

 

[java] view plaincopy
  1. @implementation MJViewController {  
  2.     NSArray *data;  
  3. }  
不过还是要在viewDidUnload方法中将data设置为nil,因为data是个strong指针,当不再使用一个对象时,应该设置为nil

 

[java] view plaincopy
  1. - (void)viewDidUnload {  
  2.     [super viewDidUnload];  
  3.     data = nil;  
  4. }  

二、IBOutlet

 

在ARC中,所有IBOutlet属性都推荐使用weak, 这些view对象已经属于View Controller的view hierarchy,不需要再次定义为strong。因此,这些定义为weak的IBOutlet属性都不需要在IBOutlet中设置为nil

 

三、@property的修饰符小结

• strong : 该属性值对应 __strong 关键字,即该属性所声明的变量将成为对象的持有者,等同于"retain"
• weak : 该属性对应 __weak 关键字,与 __weak 定义的变量一致,该属性所声明的变量将没有对象的所有权,并且当对象被释放之后,对象将被自动赋值nil,记住IBOutlet应该使用weak
• unsafe_unretained : 等效于__unsafe_unretaind关键字声明的变量,等同于之前的"assign",iOS 5之前的系统用该属性代替 weak 来使用
• copy : 和之前的copy一样,复制一个对象并创建strong关联
• assign : 对象不能使用assign,但原始类型(BOOL、int、float)仍然可以使用

 

 

 

ARC之strong,weak 解释

 

先一句话总结:strong类保持他们拥有对象的活着,weak类他们拥有的对象被人家一牵就牵走,被人家一干就干死。(strong是一个好大哥所以strong,呵呵,weak是一个虚大哥所以weak,呵呵)

 

比如有一个对象是string类,实例是@“hello”

现有两个strong的string指针大哥a和b都指向了hello,现在b大哥把改成了指向@“hi”。那么这时候a大哥指向的值是什么呢,答案还是“hello”。然后,a大哥看hi不错,也指向了hi,那么现在hello就被都抛弃了,也就从内存中删除了。因为a大哥是strong的,既是retain或者copy的,这两个东西是可以使对象保存在计算机内存里的,所以如果即使b大哥抛弃hello,a大哥是有资本使@“hello”继续活下去。

 

而现在又有两个对象strong的c大哥和weak的d大哥,都指向hello,现在c大哥另有所爱,指向了之前的b大哥,同时a大哥也指向了b大哥,既现在没有strong大哥指向hello。那么现在这个weak的d大哥指向的对象就是一个屁啦,既nil。

 

strong和weak的区别
strong表示保留它指向的堆上的内存区域不再指向这块区域了。
也就是说我强力指向了一个区域,我们不再指向它的条件只有我们指向nil或者我自己也不在内存上,没有人strong指向我了,weak表示如果还没有人指向它了,它就会被清除内存,同时被指向nil,因为我不能读取不存在的东西。
weak只在IOS5.0使用
这并不是垃圾回收,我们用reference count表示堆上还有多少strong指针,当它变为0就马上释放。
 
 
 

iOS5 ARC,IBOutlets 应该定义strong还是weak

 (2012-12-27 16:50:06)
标签: 

objective-c

 

it

 
原帖:http://blog.csdn.net/yiyaaixuexi/article/details/7864974
 

写这篇文章的缘由,是因为我泡在stackoverflow上翻帖子,看到一个名为Should IBOutlets be strong or weak under ARC? 的帖子很热,而我对被采纳为标准答案的回答也有一些话要补充,我想对于每一个初识ARC模式的人来说,都会有这个疑问,所以不妨我也来和大家探讨一下。

 

有人问,在ARC下,IBOutlets到底应该定义成strong 还是 weak ?支持这个答案的人最多,答案仅是摘自官方文档的一个片段:

 

From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because:

  • Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.

  • The strong outlets are frequently specified by framework classes (for example, UIViewController’s view outlet, or NSWindowController’s window outlet).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
    

大意是说,在 ARC 中,一般outlet属性都推荐使用 weak,应该使用 strong 的 outlet 是 File's Owner连接到 nib 的顶层对象。 

什么是 File's Owner连接到 nib 的顶层对象呢?说白话一点,就是自定义的view,不是直接作为main view里面一个sub view直接显示出来,而是需要通过实例化创建出来的。你自己实例化,当然需要strong了,不然谁还替你保留对象所有权呢?

 

以上的分析都没有错,但是总觉得少了点什么。对于到底是weak 还是 strong,归根结底,还是要刨到对对象所有权的问题上,但是不便于总结出浅显易懂的规律性使用法则。于是,就会有一个又一个的特例打破文档所总结的常规,不明白规则的根是什么,还是会碰到麻烦的。

 

我来举一个简单的例子,创建一个程序入口指向navigation controller的工程,导航栏上拖2个按钮:

右侧按钮用于控制相机按钮的显示与否,按照文档的指示,我们在程序中定义这两个按钮应为weak属性

 

  1. #import   
  2.   
  3. @interface TestViewController : UIViewController  
  4. {  
  5.     BOOL isShowing;  
  6. }  
  7.   
  8. @property (nonatomic,weak)IBOutlet UIBarButtonItem *controlBtn;  
  9. @property (nonatomic,weak)IBOutlet UIBarButtonItem *cameraBtn;  
  10.   
  11. -(IBAction)controlAction:(id)sender;  
  12. @end  


 

用右侧按钮,控制相机按钮的隐藏和显示:

 

  1. #import "TestViewController.h"  
  2.   
  3. @interface TestViewController ()  
  4.   
  5. @end  
  6.   
  7. @implementation TestViewController  
  8. @synthesize cameraBtn,controlBtn;  
  9.   
  10. - (void)viewDidLoad  
  11. {  
  12.     [super viewDidLoad];  
  13.     // Do any additional setup after loading the view, typically from a nib.  
  14.     isShowing = YES;  
  15. }  
  16.   
  17. - (void)viewDidUnload  
  18. {  
  19.     [super viewDidUnload];  
  20.     // Release any retained subviews of the main view.  
  21. }  
  22.   
  23. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  
  24. {  
  25.     return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);  
  26. }  
  27.   
  28. -(IBAction)controlAction:(id)sender  
  29. {  
  30.     if (isShowing) {  
  31.         self.controlBtn.title = @"显示相机";  
  32.         self.navigationItem.leftBarButtonItem = nil;  
  33.         isShowing = NO;  
  34.     }else {  
  35.         self.controlBtn.title = @"隐藏相机";  
  36.         self.navigationItem.leftBarButtonItem = cameraBtn;  
  37.         isShowing = YES;  
  38.     }  
  39. }  
  40. @end  


 

实验结果是,第一次隐藏了相机按钮后,就再也显示不出来了。原因很简单,cameraBtn指向了空,我们丢失了cameraBtn的对象所有权。

解决问题的办法有两个:

1.不在xib或者storyboard上拖相机按钮,而是用代码创建,自己控制对象所有权

2.将 cameraBtn 定义为strong

 

我想强调的当然是第二种方法,当然了,改成strong后,相应的也需要配合ARC做下工作:

 

  1. - (void)viewDidUnload  
  2. {  
  3.     [super viewDidUnload];  
  4.     // Release any retained subviews of the main view.  
  5.     self.cameraBtn = nil;  
  6. }  

 

 

 

顺便提一下ARC其他属性的规则:

 

  • strong:等同于"retain",属性成为对象的拥有者

  • weak:属性是 weak pointer,当对象释放时会自动设置为 nil

  • unsafe_unretained:等同于之前的"assign",只有 iOS 4 才应该使用

  • copy:和之前的 copy 一样,复制一个对象并创建 strong 关联

  • assign:对象不能使用 assign,但原始类型(BOOL、int、float)仍然可以使用 


 

最后一句,记忆规则,理解规则,善用规则。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值