从内存管理来说明使用self.和不使用self的区别:
ViewController.h文件,使用Student类,代码如下:
#import <UIKit/UIKit.h>
@class Student;
@interface ViewController : UIViewController{
Student *_student;
}
@property (nonatomic, retain) Student *student;
@end
ViewController.m文件,代码:
#import "ViewController.h"
#import "Student.h"
@implementation ViewController
@synthesize student = _student;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void) dealloc
{
[_student release];
_student = nil;
[super dealloc];
}
其它的方法没有使用到,所以这里就不在显示了。
在ViewController.m的viewDidLoad方法中创建一个Student类的对象
Student *mystudent = [[Student alloc] init];
self.student = mystudent;
[mystudent release];
这是相信有人会有疑问了,问什么创建student对象要这么复杂,似乎直接使用self.student = [[Student alloc] init]; 也没有问题,不加self有时也是挺正常的呀?
接下来就需要从内存角度来分析它们之间的区别了:
1、加self的方式:
Student *mystudent = [[Student alloc] init]; //mystudent 对象 retainCount = 1;
self.student = mystudent; //student 对象 retainCount = 2;
[mystudent release]; //student 对象 retainCount = 1;
//student的property 是retain 默认使用self.student引用计数+1。
2、不加self的方式
Student *mystudent = [[Student alloc] init]; //mystudent 对象 retainCount = 1;
student = mystudent; //student 对象 retainCount = 1;
[mystudent release]; //student 对象内存已释放,如果调用,会有异常
3、加self直接赋值方式
self.student = [[Student alloc] init]; //student 对象 retainCount = 2;容易造成内存泄露
由于objective-c内存管理是根据引用计数处理的,当一个对象的引用计数为零时,gcc才会释放该内存。
转载:http://jordy.easymorse.com/?p=805
个人总结:只需要在属性初始化的时候使用self.属性,其他时候直接使用属性名就行;使用self.是 使retaincount+1,为了确保当前类对此属性具有拥有权
个人使用习惯:
@interface CustomClass : UIViewController
{
NSString *str
}
@property (retain, nonatomic) NSString *str;
@implementation CustomClass
@synthesize str;
-(void)viewDidLoad
{
//方法一 用alloc必须手动释放一次
self.str = [[NSString alloc]initWithString:@"my str"];
[str release];
//方法二 用类方法不用
self.str = [NSString stringWithString:@"my str"];
以后调用时直接使用str,不必使用self.str
[str appendString:@"\n"];
}
//在dealloc中必须释放
- (void)dealloc
{
//方法一
[str release];
str = nil;
//方法二
self.str = nil;
[super dealloc];
}
看图:
.h文件,view2有retain参数
.m文件,
不使用self.的方法赋值
使用self.的方法赋值
使用self.的方法赋值的retaincount是对的。
@property 关键字的作用是 给实例变量在.h文件里创建一个set/get函数的申明。等同在.h文件里面给实例变量写set/get函数申明。
@property (nonatomic,retain) UIView *view2;
等于
-(UIView*)getView2;
-(void)setView2:(UIView*)view;
在.m文件里的@synthesize关键字就是自动实现set/get函数。
get方法是:
-(MyObject*)myObject{
return _myObject;
}
Set方法是:
// assign
-(void)setMyObject:(id)newValue{
_myObject = newValue;
}
// retain
-(void)setMyObject:(id)newValue{
if (_myObject != newValue) { // 若先前已经赋予相同的值,则不会引起计数器变化
[_myObject release];
_myObject = [newValue retain]; // newValue的计数器值为1,再次retain后变为2,所以self.计数器会加1
}
}
// copy
-(void)setMyObject:(id)newValue{
if (_myObject != newValue) { // 同上
[_myObject release];
_myObject = [newValue copy]; // 同上
}
}
retain关键字的作用是,在实现set函数内部,retain当前变量,具体实现是这样:
-(void)setView2:(UIView*)view{
[_view2 release];
_view2 = [view retain];
}
而self.view2 = tView;等于[self setView2:tview];也就是说,如果不用self.调用实例变量,retain关键字是不起作用的。
执行代码self.view2 = tView之后,tView和view2都是同一个对象,只是这个赋值的发操作把tView的retaincount+1了而已。所以当 [tView release]之后,self.view2的retaincount-1。
然后当再次给 self.view2赋值的时候,在set内部会再次release。这样是不会有内存泄露的。
在不使用self.的图片里,为什么在 [tView release]之后,retaincount还是1呢?这是因为 [tView release]之后, tView retaincount实际变成了0,然后tView 就被释放掉了。你后面调用tView.retainCount其实是没意义的,tView已经被释放了。所以你看到的1其实是,retainCount,retainCount2这2个变量前面被赋值的值。你如果改成赋值给一个新的变量,你就会发现是0了。而这个0其实是新的变量的初始化的值0,并不是tView.retainCount返回的0。
这里release会挂掉,但是我把[tView release]写到最后面却不会挂掉。
这里该怎么管理?
不是alloc后,赋给了_view2就该立刻release的嘛?
还有,原来那个问题真的和你说得一样,在不使用self.的时候那个reatinCount真的是上面的值,release后还是上面那个值。
你追问的图片里的红字理解是正确的。 使用self.的作用就是在于 调用set函数去给实例变量赋值。 不使用self. 直接使用_view2赋值就是等于直接给实例变量赋值,不会经过set函数。 在有retain关键字的情况下,set函数里面实现了传入新参数的retain,旧参数的release。所以这样可以更好的管理传参时的内存管理。这就是为什么Apple推荐大家都用self.进行实例变量的赋值。不到万不得已的情况,不要随便直接使用_view2实例变量直接赋值。在遵循使用self.规则前提下,self.view2 = tView;语句之后 就可以立刻对[tView release]. 但是你的代码是直接使用_view2赋值的,没进过set函数,没了tView 没有retain,后面你再[tView release],其实这里tView 就已经释放了。然后你再去访问tView 的retainCount属性,必然是要crash的。
- (void)setView2:(UIView *)view2{
[view2 retain];
//view2.retainCount == 2
[_view2 release];
//_view2.retainCount == 0
_view2 = view2;
//_view2.retainCount == 2
}
view2作为属性会自动生成setter和getter方法。self.view2 = tview;会调用上方的setter方法。
那么不使用self.赋值的时候是不是不会调用上面的setter方法来retain呢?
是的你可以试下 _view2 = tview;
self.会默认调用get方法,比如一个变量定义成了retain,则self.之后引用计数就会加1. strong,weak是arc的时候用到的。
官方的一种常见写法:
- MyClass.h
- @interface
MyClass : NSObject { -
MyObject * _myObject; - }
- @property
(nonatomic, retain) MyObject *myObject; - @end
- MyClass.m
- @synthesize
myObject = _myObject;
OK, 你现在再试下, 如果你用self._myObject = aMyObject; 或者 myObject = aMyObject; 你会得到一个错误, 为什么呢, 这里就是和Obj-c的存取方法有关了. 说白了很简单 , 大家都知道, @property (nonatomic, retain) MyObject *myObject; 是为一个属性设置存取方法, 只是平时我们用的方法名和属性名是一样的,现在你把它写成不同的名字, 就会很清楚了. _myObject是属性本身, myObject是存取方法名.
现在我们知道self.是访问属性的存取方法了, 那存取方法又怎么工作的? self.myObject = [[MyObject alloc] init]; 为什么会有内存泄露?
get方法是:
- -(MyObject*)myObject{
-
return _myObject; - }
Set方法是:
- //
assign - -(void)setMyObject:(id)newValue{
-
_myObject = newValue; - }
- //
retain - -(void)setMyObject:(id)newValue{
-
if (_myObject != newValue) { -
[_myObject release]; -
_myObject = [newValue retain]; -
} - }
- //
copy - -(void)setMyObject:(id)newValue{
-
if (_myObject != newValue) { -
[_myObject release]; -
_myObject = [newValue copy]; -
} - }
其实这些方法里还有别的内容, 并不只是这些. 而且这些方法可以被重写. 比如你写一个
- -(MyObject*)myObject{
-
return _myObject; - }
放在你的类里, 你调用self.myObject时(不要把它放在等号左边, 那会调用get方法)就会调用这个方法.
这里多说一句, @property 是为你设置存取方法, 和你的属性无关, 你可以只写一句
- @property
(readonly) NSString *name;
在你的类里实现
- -(NSString*)name{
-
NSLog(@"name"); -
return @"MyClass"; - }
同样可以用self.name调用.
顺便说一下, 如果你想在其它类访问对象属性, 而不是通过存取方法, 你可以用myClass -> myObject来访问, 这样是直接访问对象本身, 不过你先要把myObject设成@public. 但这个是官方不提倡的,