52个有效方法(7) - 在对象内部尽量直接访问实例变量
程序员文章站
2023-11-14 18:48:04
1. 在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
2. 在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。
3. 使用Lazy Initialization配置的数据,应该通过属性来读取数据。
4. 不要在setter/g... ......
在对象内部尽量直接访问实例变量
在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
-
_name = @"jack"
不经过setter的消息发送,直接为变量赋值,速度快。
对于以下的 name 属性:@property (nonatomic, copy) nsstring *name;
直接赋值是:_name = @"jack"
; ,通过self.name = @"jack"
其实等同于_name = @"jack".copy
。 -
self.name = @"jack"
会触发kvo,_name = @"jack"
不会
-self.name = @"jack"
可以在 setter 方法中进行断点调试,每次赋值你都知道。 - 所以有一种合理折中方案就是,读取数据的时候用
nsstring *str = _name
,赋值用self.name = @"jack"
。
在对象内部访问实例变量时,是通过属性(self.proper
)来访问还是通过_proper
来访问区别在于是否执行属性的setter
、getter
方法。
如果执行属性的
setter
、getter
方法,则通过_proper
来访问。- 如果未执行属性的
setter
、getter
方法,则通过属性(self.proper
)来访问。
@interface wrestler : nsobject @property (copy, nonatomic) nsstring *name; // 将name声明为属性 - (void)smell; @end @implementation wrestler @synthesize name = _name; // 属性name可以使用实例变量_name直接访问 - (void)setname:(nsstring *)aname { nslog(@"set name"); _name = [aname copy]; } - (nsstring *)name { nslog(@"get name"); return [_name copy]; } - (void)smell { nslog(@"*** smelling ***"); // 使用dot syntax访问实例变量 nslog(@"%@", self.name); // 直接调用属性的getter方法 nslog(@"%@", [self name]);
在初始化方法及dealloc
方法中,总是应该直接通过实例变量来读写数据。
子类可能复写
setter
方法,用self.proper = @""
可能不等同于_proper = @"".copy
。- 我们写一个
wrestler
的子类cena
,该类继承了属性name
并重写了其setter
方法,该方法会先检验名字后缀是否为cena
,否则抛出异常。
@interface cena : wrestler - (instancetype)initwithname:(nsstring *)aname; - (void)wrestle; @end @implementation cena @synthesize name = _name; - (instancetype)initwithname:(nsstring *)aname { self = [super init]; if (self) { nslog(@"self.name = aname"); self.name = aname; } return self; } - (void)wrestle { nslog(@"i'm %@, u can't see me", self.name); } - (void)setname:(nsstring *)aname { if (![aname hassuffix:@"cena"]) { [nsexception raise:nsinvalidargumentexception format:@"last name must be cena"]; } _name = [aname copy]; } @end
- 在父类wrestler的init方法中将name初始化为空白字符串@""
(instancetype)init { self = [super init]; if (self) { nslog(@"self.name = empty string"); self.name = @""; } return self;
- 调用
cena *cena = [[cena alloc] initwithname:@"john cena"]; [cena wrestle];
- 运行崩溃。原因:
self.name = @"";
。调用子类中覆写的name
的setter
方法,空白字符串明显没有@"cena"
后缀,从而抛出异常。
使用lazy initialization
配置的数据,应该通过属性来读取数据。
@property (strong, nonatomic) nsnumber *chamcount; - (nsnumber *)chamcount { if (!_chamcount) { _chamcount = @13; } return _chamcount;
不要在setter/getter方法中调用setter/getter
方法
- 将上面的setter方法修改:
- (void)setname:(nsstring *)aname { nslog(@"set name"); // _name = [aname copy]; self.name = aname; }
- 运行程序,控制台不停输出
set name
,崩溃。 -
原因:在
setter
方法中调用setter方法会不断嵌套调用,最终导致程序崩溃。getter方法同理。
要点
在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。
使用lazy initialization配置的数据,应该通过属性来读取数据。
不要在setter/getter方法中调用setter/getter方法。
下一篇: Python3.5 创建文件的简单实例