关于@synthesis、变量、属性、属性名前面下划线的问题

关于@synthesis、变量、属性、属性名前面下划线的问题

那个下划线只是为了方便区别全局变量和本地变量,或者防止变量重名,只是一种编程风格,实际上对代码没有实质影响,更无法替代@synthesize语句的功能。你只要记住一个真理,

凡是在头文件里面定义了@property的变量,必定要@synthesize。


第二:字符或变量前面有双下划线的:

__strongstatic id _sharedObject =nil;//双下划线

就是一个变量名,只不过是用在内部的,所以用了“_ _”,以区分用户自己定义的变量

系统自己的变量一般都以下划线开头,以示区分。 
自己定义的名字最好不要在前面加下划线,以免不小心和人家的函数重名~


很少会见到 __weak 和 __strong 出现在声明中,默认情况下,一个指针都会使用 __strong 属性,表明这是一个强引用。这意味着,只要引用存在,对象就不能被销毁。这是一种所期望的行为:当所有(强)引用都去除时,对象才能被收集和释放。不过, 有时我们却希望禁用这种行为:一些集合类不应该增加其元素的引用,因为这会引起对象无法释放。在这种情况下,我们需要使用弱引用(不用担心,内置的集合类 就是这么干的),使用 __weak 关键字。NSHashTable 就是一个例子。当被引用的对象消失时,弱引用会自动设置为 nil。Cocoa 的 Noti?cation Center 就是这么一个例子,虽然这已经超出纯 Objective-C 的语言范畴 


看到很多源代码里面,使用前面带下划线变量,然后在@synthesize 语句中在用一个不带下划线的变量名。这样做,到底有什么作用?
因为我常常是以这种方式来做的:
*.h中申明变量

<span style="font-family: Tahoma;">#import <UIKit/UIKit.h>

@interface NewPlayerController : UIViewController{
    NSString *test;
}

@property(nonatomic,retain) NSString *test;

@end</span>
在*.m中
<span style="font-family: Tahoma;">#import "NewPlayerController.h"

@implementation NewPlayerController
@synthesize test;
- (void)viewDidLoad{

    [super viewDidLoad];

    test=[[NSString alloc] initWithFormat:@"test"];
}
@end</span>
但是,发现很多别人写的代码是这样子的:
*.h中申明变量
<span style="font-family: Tahoma;">#import <UIKit/UIKit.h>

@interface NewPlayerController : UIViewController{
    NSString* _test;
}

@property(nonatomic,retain) NSString *test;

@end</span>
在*.m中
<span style="font-family: Tahoma;">#import "NewPlayerController.h"

@implementation NewPlayerController

@synthesize test=_test;

- (void)viewDidLoad{

    [super viewDidLoad];

    _test=[[NSString alloc] initWithFormat:@"test"];

    // 或者这样
    self.test=[[NSString alloc] initWithFormat:@"test"];
}

@end</span>
这两种方式到底有什么区别?用那种好?
第一种方式,就是平常我们说的不带下划线的那种方式:
<span style="font-family: Tahoma;">self.test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的应用计数:%d",[self.test retainCount]);
NSLog(@"test的应用计数:%d",[test retainCount]);</span>
但是,我惊奇的发现,输出竟然这样:
<span style="font-family: Tahoma;">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</span>
self.test和test的地址是相同的,说明是同一个对象的应用,但为什么,应用计数会是2呢?
于是,代码变成这样写(仅仅是,self.test换成test):
<span style="font-family: Tahoma;">test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的应用计数:%d",[self.test retainCount]);
NSLog(@"test的应用计数:%d",[test retainCount]);</span>
输出:
<span style="font-family: Tahoma;">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</span>
这才是我们所期望的。应用计数应该为1才对,为什么会变成2呢?恍然大悟了!原来原因出在这里:我们申明test时,用的属性修饰符retain。
当我们,使用self.test时,就使用了编译器为我们生成的setXXX方法。在该方法中retainCount被加1,所以,变为2.
这样的花,如果,我们在代码中,直接用self.test=[[NSString alloc] initWithFormat:@"test"];这行代码后,test变量的应用计数变成了2:
相当于:
首先,声明一个字符串对象,这时候,引用计数为1.
其次,再将test的值赋给self.test。(相当于,使用了setXXX方法,让retainCount,加1),导致应用计数变成2.
所以,在类中,如果,仅仅是指针赋值,(将一个对象的指针赋给另一个指针)尽量避免使用self.test进行赋值。这样会引起引用计数+1,容易引起释放内存泄漏。而直接用test来赋值。
也就是,在方法中,使用self.xxxx,进行赋值,就会使用编译器生成的setXXX方法,从而根据申明对象时的属性(copy,retain,assign)进行调用setXXX方法。
如果,只用属性之间来赋值(test= newValue;,就是不带self),那么,仅仅是指针之间的赋值。
第二种方式,就是带下划线的那种方式:代码中,我们这么写:
test=[[NSString alloc] initWithFormat:@"test"];
我们会收到一个错误提示.看来,不能直接访问test,而要用_test来代替。那就用它推荐的方法,继续写完:
<span style="font-family: Tahoma;">self.test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的应用计数:%d",[self.test retainCount]);
NSLog(@"_test的应用计数:%d",[_test retainCount]);</span>
运行代码,输出如下:
<span style="font-family: Tahoma;">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</span>
运行输出,很显然,应用计数为2,不是我们想要的结果。原因,跟我们讨论的第一种情况,是一样的。
这仅仅是一种调用的另一种方式:
默认情况,调用test,当我们申明test=_test;时候,那么在类内部使用_test。
在使用_test,时候,也仅仅是指针的赋值。
使用self.test,就是要调用编译器生成的响应的getXXX,setXXX方法了。
为了证明我们的想法,我们将代码修改成这样:
<span style="font-family: Tahoma;">_test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的应用计数:%d",[self.test retainCount]);
NSLog(@"_test的应用计数:%d",[_test retainCount]);</span>
输出如下:
<span style="font-family: Tahoma;">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</span>
但是,我们用那种方式好呢????
我看了一些帖子,说,苹果不提倡在我们定义的类中使用“_xxxx”,这种形式的属性声明,因为,在很多苹果提供的框架中,大量使用“_xxxx”这种属性声明。

——————————————————————————————————————
Cocoa编码指南: 编程公约:
避免使用下划线作为前缀,特别是在私有方法中。苹果公司保留并使用本公约。使用第三方可能会导致命名空间冲突;自己不知不觉中,可能会覆盖现有的私有方法,带来灾难性的后果。
——————————————————————————————————————
在这种情况下,如果,我们,继承了某个框架类,并且无意间声明的“_xxxx”属性跟父类的相同,那么可能就会覆盖掉,从而引发不可估计的后果!
但是,我看苹果帮助文档中写的许多例子也在用“_xxxx”这种方式啊!所以,我觉的可以用,但要注意!Object-C <wbr>声明属性为什么用下划线,代码规范和编程风格
为什么是可以用呢?
因为个人感觉:“_xxxx”确实很好用!
1.从风格上表明类的内部变量。
2.意在指明这个变量是内部变量(类外部不会使用。。。)
3.外部访问用obj.xxxxx, 避免对类变量的直接访问。
4.这样的话,要是需要直接引用变量就用_xxxx,当需要用get,set方法时,就用self.xxx。
汇成一句话:
下划线和非下划线的使用,可以说是一种习惯问题吧。不用太过于纠结!
默认情况下,@synthesize name;编译器为我们生成的get,set方法中所使用的变量名称,跟我们申明的变量名称时一样的(仅仅用self.name和name来区分确实不够理想)。

但是,当我们用@synthesize name=_name;时,就为属性取了一个别名,那样的话,指针变量,跟编译器生成的get,set方法为属性赋值时就容易区分了!


-----------------------------------------------------

我们在进行iOS开发时,经常会在类的声明部分看见类似于 @synthesize window=_window;  的语句,那么,这个 window是什么,_  window又是什么,两个东西分别怎么用,这是一个比较基本的问题,也关乎我们理解 Objective-C中对类、类的属性、类的存取器、类的局部变量的统一理解。
在32位系统中,如果类的 @interface 部分没有进行 ivar 声明,但有 @property 声明,在类的 @implementation 部分有响应的 @synthesize,则会得到类似下面的编译错误:
Synthesized property 'xX' must either be named the same as a compatible ivar or must explicitly name an ivar
在 64-bit时,运行时系统会自动给类添加 ivar,添加的 ivar 以一个下划线"_"做前缀。
上面声明部分的 @synthesize window=_window; 意思是说,window 属性为 _window 实例变量合成访问器方法。
也就是说,window属性生成存取方法是setWindow,这个setWindow方法就是_window变量的存取方法,它操作的就是_window这个变量。通过这个看似是赋值的这样一个操作,我们可以在 @synthesize 中定义与变量名不相同的getter和setter的命名,籍此来保护变量不会被不恰当的访问。

下面是一个常见的例子
写法一:
  1. @interface MyClass:NSObject{    
  2.         MyObjecct *_myObject;  
  3. }  
  4. @property(nonamtic, retain) MyObjecct *myObject;  
  5. @end  
  6.   
  7. @implementatin MyClass  
  8. @synthesize myObject=_myObject;  
写法二:
  1. @interface MyClass:NSObject{  
  2.         
  3. }  
  4. @property(nonamtic, retain) MyObjecct *myObject;  
  5. @end  
  6.   
  7. @implementatin MyClass  
  8. @synthesize myObject=_myObject;  
这个类中声明了一个变量_myObject,又声明了一个属性叫myObject,然后用@synthesize生成了属性myObject的存取方法,这个存取方法的名字应该是:setmyObject和getmyObject。@synthesize myObject=_myObject的含义就是属性myObject的存取方法是做用于_myObject这个变量的。这种用法在Apple的Sample Code中很常见。
弄明白了这个语句的意思之后,我们也就清楚了myObject和_myObject的区别,那么,在使用的时候,有什么需要注意的地方,大家应该也都清楚了。是的,myObject是属性,而_ myObject才是变量,我们最终操作的变量都是myObject。
那么,同样是存取操作,语句
  1. self.nameVarPtr = [[ObjectName alloc] init]   
  1. nameVarPtr = [[ObjectName alloc] init]  
两种赋值方式的区别何在呢?

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; 这才是最可靠的方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值