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方法。