关于@synthesis、变量、属性、属性名前面下划线的问题
那个下划线只是为了方便区别全局变量和本地变量,或者防止变量重名,只是一种编程风格,实际上对代码没有实质影响,更无法替代@synthesize语句的功能。你只要记住一个真理,
凡是在头文件里面定义了@property的变量,必定要@synthesize。
第二:字符或变量前面有双下划线的:
__strongstatic id _sharedObject =nil;//双下划线
就是一个变量名,只不过是用在内部的,所以用了“_ _”,以区分用户自己定义的变量
系统自己的变量一般都以下划线开头,以示区分。
自己定义的名字最好不要在前面加下划线,以免不小心和人家的函数重名~
很少会见到 __weak 和 __strong 出现在声明中,默认情况下,一个指针都会使用 __strong 属性,表明这是一个强引用。这意味着,只要引用存在,对象就不能被销毁。这是一种所期望的行为:当所有(强)引用都去除时,对象才能被收集和释放。不过, 有时我们却希望禁用这种行为:一些集合类不应该增加其元素的引用,因为这会引起对象无法释放。在这种情况下,我们需要使用弱引用(不用担心,内置的集合类 就是这么干的),使用 __weak 关键字。NSHashTable 就是一个例子。当被引用的对象消失时,弱引用会自动设置为 nil。Cocoa 的 Noti?cation Center 就是这么一个例子,虽然这已经超出纯 Objective-C 的语言范畴
看到很多源代码里面,使用前面带下划线变量,然后在@synthesize 语句中在用一个不带下划线的变量名。这样做,到底有什么作用?
因为我常常是以这种方式来做的:
*.h中申明变量
#import <UIKit/UIKit.h> @interface NewPlayerController : UIViewController{ NSString *test; } @property(nonatomic,retain) NSString *test; @end
在*.m中#import "NewPlayerController.h" @implementation NewPlayerController @synthesize test; - (void)viewDidLoad{ [super viewDidLoad]; test=[[NSString alloc] initWithFormat:@"test"]; } @end
但是,发现很多别人写的代码是这样子的:#import <UIKit/UIKit.h> @interface NewPlayerController : UIViewController{ NSString* _test; } @property(nonatomic,retain) NSString *test; @end
在*.m中#import "NewPlayerController.h" @implementation NewPlayerController @synthesize test=_test; - (void)viewDidLoad{ [super viewDidLoad]; _test=[[NSString alloc] initWithFormat:@"test"]; // 或者这样 self.test=[[NSString alloc] initWithFormat:@"test"]; } @end
这两种方式到底有什么区别?用那种好?self.test=[[NSString alloc] initWithFormat:@"test"]; NSLog(@"self.test的应用计数:%d",[self.test retainCount]); NSLog(@"test的应用计数:%d",[test retainCount]);
但是,我惊奇的发现,输出竟然这样:2012-11-17 15:52:29.604 ArtTV[901:14303] self.test的应用计数:2 2012-11-17 15:52:33.264 ArtTV[901:14303] test的应用计数:2
self.test和test的地址是相同的,说明是同一个对象的应用,但为什么,应用计数会是2呢?test=[[NSString alloc] initWithFormat:@"test"]; NSLog(@"self.test的应用计数:%d",[self.test retainCount]); NSLog(@"test的应用计数:%d",[test retainCount]);
输出:2012-11-17 15:59:58.274 ArtTV[954:14303] self.test的应用计数:1 2012-11-17 15:59:59.718 ArtTV[954:14303] test的应用计数:1
这才是我们所期望的。应用计数应该为1才对,为什么会变成2呢?恍然大悟了!原来原因出在这里:我们申明test时,用的属性修饰符retain。self.test=[[NSString alloc] initWithFormat:@"test"]; NSLog(@"self.test的应用计数:%d",[self.test retainCount]); NSLog(@"_test的应用计数:%d",[_test retainCount]);
运行代码,输出如下:2012-11-17 16:30:39.525 ArtTV[1042:14303] self.test的应用计数:2 2012-11-17 16:30:41.553 ArtTV[1042:14303] _test的应用计数:2
运行输出,很显然,应用计数为2,不是我们想要的结果。原因,跟我们讨论的第一种情况,是一样的。_test=[[NSString alloc] initWithFormat:@"test"]; NSLog(@"self.test的应用计数:%d",[self.test retainCount]); NSLog(@"_test的应用计数:%d",[_test retainCount]);
输出如下:2012-11-17 16:36:09.089 ArtTV[1074:14303] self.test的应用计数:1 2012-11-17 16:36:10.701 ArtTV[1074:14303] _test的应用计数:1
但是,我们用那种方式好呢????但是,当我们用@synthesize name=_name;时,就为属性取了一个别名,那样的话,指针变量,跟编译器生成的get,set方法为属性赋值时就容易区分了!
self.nameVarPtr=xxx 这种赋值方式等价于调用 [self setnameVarPtr:xxx], 而setnameVarPtr:xxx的方法的实现又是依赖于@property的属性的,比如retain,assign等属性。
nameVarPtr = xxx 的赋值方式,仅仅是对一个指针进行赋值。nameVarPtr仅仅是一个指针变量,记录了xxx的地址。在这个过程中不会调用setter方法,不会调用setter方法,就和@property没有关系,从而,也和retain,assign等属性没有关系。这种赋值方式就是一个简单的指针赋值。
综上,对成员变量进行赋值,为防内存泄露需要注意的点:
1.self调用setter方法的方式
ObjectName* tmp= [[ObjectName alloc] init];
self.nameVarPtr =tmp; //retainCount=2
[tmp release]; //retainCount=1
2.指针赋值方式,不会调用setter方法
nameVarPtr= [[ObjectName alloc] init]; // retainCount=1
所以,笔者建议大家在对某个变量进行赋值操作的时候,尽量要写self.myObj = xxx; 这才是最可靠的方法。