不要在init和dealloc函数中使用accessor
2015-03-18 14:00
232 查看
Objective-C 2.0 增加了dot syntax,用于简单地调用成员变量的accessor。相当于java的getter和setter。因为正常情况下,写一个accessor对于初学者来说,还是挺容易犯错的。比如有一个NSString * 的成员变量叫name。一个错误的写法是:
- (void) setName:(NSString *)newName
{
name = newName;
}
错误原因是Objective-C需要自己负责内存的释放。所以需要在改变reference之前,将原对象release,对新的对象,也需要retain一下,代码就改成这样:
- (void) setName:(NSString *)newName
{
[name release];
name = [newName retain];
}
初学者可能以为这样就对了,其实还是有错,如果newName和name的指向的是同一个对象,并且这个对象retain count只有1的话。那么name release之后,这个对象就被回收掉了。所以应该改成:
- (void) setName:(NSString *)newName
{
if (name != newName) {
[name release];
name = [newName retain];
}
}
这样才算是一个正确的set函数,Java同学肯定被吓到了,虽然知道这么写,但这比Java麻烦多了。于是,Objective-C允许程序员使用 @property + @synthesize 关键字来自动生成这些代码。于是Objective-C的程序员幸福了。大部分时候根本就不用写getter和setter。
但是需要小心,Objective-C的accessor不能在init和dealloc函数中使用!
Apple在Mac与iOS中关于内存管理的开发文档中,有一节的题目为:“Don’t
Use Accessor Methods in Initializer Methods and dealloc”,文中说:“The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc.”但是并没有解释为什么。
大牛“唐巧”《http://blog.devtang.com/blog/2011/08/10/do-not-use-accessor-in-init-and-dealloc-method/》总结了下有两种可能的原因:“比较不靠谱的说法是这样少一次函数调用,更快;比较靠谱的说法是:在init和dealloc中,对象的存在与否还不确定,所以给对象发消息可能不会成功。”
下面这则代码说明了一种可能会引起错误的情况:父类在init中使用了value的setter,而子类重写了value的setter,而子类的init中会首先调用父类的init,这样就会导致子类value的setter会先于子类自己的init代码调用,就有可能会出现问题。这则代码就会在_info初始化之前进行操作。
造成这个问题的原因有两个:一就是在init使用了setter;二是子类重写了setter,导致在父类init时就会调用子类重写的setter,万一重写的setter中进行了一些子类特有的操作就可能会出现问题。
实际上两个条件很难同时满足,但万一不小心满足就很难发现这个错误。不是说一定不能在init或dealloc中使用accessor(如果父类变量是private,子类只能使用accessor),而是在使用的时候一定要明白可能会导致的问题,不要死记各种规则,而要真正理解背后的原理。
#import <Foundation/Foundation.h>
@interface Parent : NSObject
{
@protected
int _value;
}
@property (nonatomic, assign) int value;
@end
@implementation Parent
@synthesize value = _value;
- (id)init {
self = [super init];
if (self) {
// Initialize self.
self.value = 1;
}
return self;
}
@end
@interface Child : Parent
{
NSString *_info;
}
@end
@implementation Child
- (id)init {
self = [super init];
if (self) {
// Initialize self.
_info = @"child";
}
return self;
}
- (void) setValue:(int)value
{
_value = value;
NSLog(@"%@", _info);
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
// insert code here…
Child *child = [[Child alloc] init];
NSLog(@"%d", child.value);
[child release];
}
return 0;
}
- (void) setName:(NSString *)newName
{
name = newName;
}
错误原因是Objective-C需要自己负责内存的释放。所以需要在改变reference之前,将原对象release,对新的对象,也需要retain一下,代码就改成这样:
- (void) setName:(NSString *)newName
{
[name release];
name = [newName retain];
}
初学者可能以为这样就对了,其实还是有错,如果newName和name的指向的是同一个对象,并且这个对象retain count只有1的话。那么name release之后,这个对象就被回收掉了。所以应该改成:
- (void) setName:(NSString *)newName
{
if (name != newName) {
[name release];
name = [newName retain];
}
}
这样才算是一个正确的set函数,Java同学肯定被吓到了,虽然知道这么写,但这比Java麻烦多了。于是,Objective-C允许程序员使用 @property + @synthesize 关键字来自动生成这些代码。于是Objective-C的程序员幸福了。大部分时候根本就不用写getter和setter。
但是需要小心,Objective-C的accessor不能在init和dealloc函数中使用!
Apple在Mac与iOS中关于内存管理的开发文档中,有一节的题目为:“Don’t
Use Accessor Methods in Initializer Methods and dealloc”,文中说:“The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc.”但是并没有解释为什么。
大牛“唐巧”《http://blog.devtang.com/blog/2011/08/10/do-not-use-accessor-in-init-and-dealloc-method/》总结了下有两种可能的原因:“比较不靠谱的说法是这样少一次函数调用,更快;比较靠谱的说法是:在init和dealloc中,对象的存在与否还不确定,所以给对象发消息可能不会成功。”
下面这则代码说明了一种可能会引起错误的情况:父类在init中使用了value的setter,而子类重写了value的setter,而子类的init中会首先调用父类的init,这样就会导致子类value的setter会先于子类自己的init代码调用,就有可能会出现问题。这则代码就会在_info初始化之前进行操作。
造成这个问题的原因有两个:一就是在init使用了setter;二是子类重写了setter,导致在父类init时就会调用子类重写的setter,万一重写的setter中进行了一些子类特有的操作就可能会出现问题。
实际上两个条件很难同时满足,但万一不小心满足就很难发现这个错误。不是说一定不能在init或dealloc中使用accessor(如果父类变量是private,子类只能使用accessor),而是在使用的时候一定要明白可能会导致的问题,不要死记各种规则,而要真正理解背后的原理。
#import <Foundation/Foundation.h>
@interface Parent : NSObject
{
@protected
int _value;
}
@property (nonatomic, assign) int value;
@end
@implementation Parent
@synthesize value = _value;
- (id)init {
self = [super init];
if (self) {
// Initialize self.
self.value = 1;
}
return self;
}
@end
@interface Child : Parent
{
NSString *_info;
}
@end
@implementation Child
- (id)init {
self = [super init];
if (self) {
// Initialize self.
_info = @"child";
}
return self;
}
- (void) setValue:(int)value
{
_value = value;
NSLog(@"%@", _info);
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
// insert code here…
Child *child = [[Child alloc] init];
NSLog(@"%d", child.value);
[child release];
}
return 0;
}
相关文章推荐
- setter方法的内部实现【不要在init和dealloc函数中使用accessor】
- 不要在init和dealloc函数中使用accessor
- 不要在init和dealloc函数中使用accessor
- 不要在init和dealloc函数中使用accessor
- 不要在init和dealloc函数中使用accessor
- 不要在init和dealloc函数中使用accessor(访问器)
- 不要在init和dealloc函数中使用accessor
- 不要在init和dealloc函数中使用accessor
- 不要在init和dealloc函数中使用accessor
- 不要在init和dealloc函数中使用Accessor 方法[Don’t Use Accessor Methods in Initializer Methods and dealloc]
- 不要在init和dealloc函数中使用accessor
- 不要在init和dealloc函数中使用accessor
- 为什么不能在init和dealloc函数中使用accessor方法
- 为什么不能在init和dealloc函数中使用accessor方法
- init和dealloc中使用property(accessor)的副作用
- Objective-C, 为什么不能在init或是dealloc方法中使用accessor方法
- 千万不要使用6位密码!密码破解速度全面披露
- 不要使用Windows的系统时间做数据排序
- 不要使用Microsoft Project的理由
- 千万不要使用6位密码!密码破解速度全面披露