在Objective-C的類與對象的概念中. 成員變量與屬性的區別與聯系一直沒有搞清楚. 直到學習了慕課網上的這個課程Objective-C面向對象初體驗, 才算真正有了點感覺了. 最關鍵的結論就是: 類內使用成員變量{}, 類外使用屬性@property.
首先, 我們來看下基本的類成員變量及其使用.
// People.h
@interface People : NSObject
{
NSString *_peopleName;
}
@end
在.m中不做任何事情, 然後在main.m調用_peopleName成員變量,
(下圖可以看出, 調用類的成員變量時, 使用 . 語法符號會出錯, 必須使用->來調用):
調用類成員變量 title=>
改為->, 調用p1->_peopleName的結果如下:
即, 該_peopleName默認是protected, 外部調用需要設置為@public. 改動一下:<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
// People.h
@interface People : NSObject
{
@public
NSString *_peopleName;
}
@end
調用p1->_peopleName的結果如下:
2015-05-06 15:58:41.039 memberAndProperty[2851:304100] p1._peopleName : (null)
如果想在init中初始化_peopleName, 則在People.m中:
// People.m
- (instancetype)init
{
self = [super init];
if (self) {
_peopleName = @people name 1;
}
return self;
}
調用p1->_peopleName的結果如下:
2015-05-06 16:01:36.974 memberAndProperty[2895:306281] p1._peopleName : people name 1
其他使用該成員變量的類內部方法都是類似的用法.
以上是對類成員變量_peopleName的調用, 如果想要對其附新值呢?
// main.m
People *p1 = [[People alloc] init];
NSLog(@p1._peopleName : %@, p1->_peopleName);
p1->_peopleName = @people name 2;
NSLog(@p1._peopleName : %@, p1->_peopleName);
結果:
2015-05-06 16:05:34.915 memberAndProperty[2931:309406] p1._peopleName : people name 1
2015-05-06 16:05:34.916 memberAndProperty[2931:309406] p1._peopleName : people name 2
以上可見, 將_peopleName設置為@public之後, 可在類外對其進行get/set操作, 直接調用或賦值即可.
但不推薦使用p1->_peopleName這樣的形式. 因為, 這樣就不符合我們所說的
“類內使用成員變量{}, 類外使用屬性@property“的結論了. 且將成員變量_peopleName設為@public會很不安全.
仍然將成員變量_peopleName默認為@protected, 從類內部的方法中對_peopleName進行讀取或賦值, 然後間接傳遞至類外部, 是一個不錯的選擇.
首先, 在.h中聲明getName和setName方法:
// People.h
@interface People : NSObject
{
NSString *_peopleName;
}
-(NSString *)getName;
-(void)setName:(NSString *)name;
@end
在.m中, get/set這兩個方法是以在類內部對成員變量_peopleName進行調用或賦值的方式來實現的.
// People.m
-(NSString *)getName {
return _peopleName;
}
-(void)setName:(NSString *)name {
_peopleName = name;
}
那麼, 調用的時候就非常方便了:
// main.m
People *p1 = [[People alloc] init];
NSLog(@p1.getName : %@, p1.getName);
[p1 setName:@people name 2];
NSLog(@p1.getName : %@, p1.getName);
結果如下:
2015-05-06 16:25:36.019 memberAndProperty[3036:320317] p1.getName : people name 1
2015-05-06 16:25:36.020 memberAndProperty[3036:320317] p1.getName : people name 2
使用自定義的get/set, 從類內部調用成員變量是一種比較常見的方式. 但需手動添加這兩個方法.
那麼, 有沒有更加簡便的方法呢? 或者說, get/set這種非常常見的操作, 能不能默認提供給我們呢? 答案是肯定的!
在新的iOS SDK中, 使用@property來定義類的屬性, 是專用於從類外部對其進行調用或賦值的.
在.h中先聲明peopleName屬性:
// People.h
@interface People : NSObject
{
NSString *_peopleName;
}
@property(nonatomic, strong) NSString *peopleName;
// nonatomic 非原子性訪問, 不加同步機制, 多線程並非訪問時可提高性能
// strong 相當於一個深拷貝的操作
@end
在.m中使用@synthesize指令將peopleName屬性與_peopleName成員變量關聯起來:
// People.m
@implementation People
@synthesize peopleName = _peopleName;
- (instancetype)init
{
self = [super init];
if (self) {
_peopleName = @people name 1;
}
return self;
}
@end
即, 編譯器遇到@synthesize peopleName = _peopleName;時, 會自動生成對peopleName的get/set方法.
且這裡的下劃線_是必不可少的, 否則就不能正確地將屬性與成員變量關聯起來.
然後, 我們直接調用即可:
// main.m
People *p1 = [[People alloc] init];
NSLog(@p1.peopleName : %@, p1.peopleName);
p1.peopleName = @people name 2;
NSLog(@p1.peopleName : %@, p1.peopleName);
結果如下:
2015-05-06 16:32:29.142 memberAndProperty[3094:325295] p1.peopleName : people name 1
2015-05-06 16:32:29.143 memberAndProperty[3094:325295] p1.peopleName : people name 2
實際上, 編譯器比我們想象中更聰明, 在.h文件中的{ NSString *_peopleName; }這個成員變量不需要聲明也可以. 僅僅聲明了@property(nonatomic, strong) NSString *peopleName;這個屬性, xcode也會默認自動為我們聲明一個該類的peopleName成員變量, 及隱藏的get/set方法. 這裡, 就體現出了下劃線 的作用了.