interface和setter,getter

前篇說到咱們經過ObjC的Category特性給平常工做增長便捷的實現,這一篇則要從語言設計角度,跟你們分享一些思考。segmentfault

不要忽視interface

ObjC的@interface設計,跟Java和C#真的很像,但又略有不一樣,相比之下Java和C#則像是一個模子刻出來的。ObjC的特色十分明顯,首先是通常不用寫@private@public來區分私有變量,大部分ObjC開發者甚至都不知道還有這兩個關鍵字,其實Cocoa源代碼中也基本沒有使用過這種設計,即便ObjC是支持的。設計模式

<!--more-->安全

在@interface 中使用 @private和@public框架

@interface Student : NSObject
{
    @private
    NSString* _name;
    @public
    NSNumber* _age;
    int _height;
}
@end

如上代碼中,Student有一個私有變量_name,和兩個共有變量_age_height,但在@interface中聲明變量,必定不是Cocoa設計者的初衷,這裏有兩個方面的考慮。atom

其一,把內部變量直接暴露在外,會下降整個框架的穩定性,由於增長不一樣模塊之間的耦合,下降了每一個類的內聚性。
其二,內部變量的變量名,很容易跟局部變量變量名產生衝突。上例中我給每個變量名前加了下劃線,就是爲了防止這個問題發生。線程

因此縱觀Cocoa框架的頭文件設計,基本沒有這樣的代碼,由於設計者提供了更好的實現方式,就是你們用的更多的@property關鍵字。設計

若是用@property聲明上面的類,你們都很熟悉code

@interface Student : NSObject

@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSNumber* age;
@property (nonatomic, assign) NSInteger height;

@end

@property這個設計真的頗有意思,首先咱們再也不區分私有公有屬性,由於只要寫在.h裏面的@property,咱們都默認是共有的,私有的@property能夠寫在.m文件裏。對象

其次,配合寫在@implementation裏面的@synthesize關鍵字,能夠自動生成setter和getter方法,而如今@synthesize關鍵字均可以省略,除了個別狀況有修改內部變量名稱的需求。blog

@implementation Student
@synthesize name = _name;
@synthesize age = __age;
@end

上面的@synthesize,第一個是能夠省略的,在不寫的狀況下,編譯預處理會自動給添加@synthesize代碼,因此即便沒有合成(synthesize)height屬性,咱們依然實現了它的setter和getter方法

//這兩個方法能夠重寫

- (void)setHeight:(NSInteger)height
{
    _height = height;
}

- (NSInteger)height
{
    return _height;
}

在setter和getter方法均重寫的狀況下,@synthesize須要手動添加。

@synthesize height = _height;

爲何要使用 setter 和 getter

settergetter的設計的確值得琢磨,咱們主要從如下幾點分析:

setter 和 getter 包裝了內部變量,整個類對外能夠只暴露接口,加強類的內聚性。

例如上例中的內部變量_name,外部類是沒法操做的,只能經過set和get接口來發消息:

Student* s = [[Student alloc] init];

[s setName:@"Tom"];
[s name];

經過實現 getter方法,能夠避開初始化變量的時機問題

這也是很實用的一個點,由於ObjC的消息設計機制,致使ObjC很難在初始化(init)方法中傳入過多參數(題外話,我給ObjC擴展過依賴注入,詳見iOS實現依賴注入)。所以新實例的默認屬性,放在什麼位置實現合適,是你們必定遇到過的問題。

例如最多見的UIViewController,代碼初始化走init方法,而經過storyboard實力化則走initWithCoder方法,一些容器屬性,經過getter方法初始化,則可避免第一次調用還沒有初始化形成的問題。

早期的Cocoa在若是給nil發消息,是會引發異常的,如今的版本給沒有alloc的對象發消息再也不拋異常,以致於某些時候屬性沒有初始化形成的問題變得更隱蔽,然而重寫getter方法能夠有效避免這個問題,例如:

//班級類
@interface XXClass : NSObject
@property (nonatomic, strong) NSMutableArray* students; //學生
@end

@implementation

//實現getter方法,在內部變量_students沒初始化的狀況下將其初始化
- (NSMutableArray *)students
{
    if (!_students) {
        _students = [NSMutableArray array];
    }
    return _students;
}

@end

如此一來,不管在任什麼時候候,第一次發送[self students]消息的時候,內部變量_students都會初始化。

在這裏要另外註明一點,在類的內部,不要在setter和getter方法外,直接使用內部變量,遵照這一條會收益不少。

setter 和 getter 能夠單獨使用,也能夠脫離內部變量使用

這裏要說的就是@property的靈活性了,你們知道@property擁有一系列的修飾詞,除了經常使用的nonatomic(非原子化,線程安全)strong(強引用類型)weak(弱引用類型)assign(賦值,用於非對象屬性)之外,還有readonly(只讀)readwrite(可讀寫)兩個影響setter和getter方法的屬性,readonly修飾的屬性,只有getter方法而沒有setter方法。

readwrite則是一個看起來無關緊要的修飾詞,由於默認就是可讀寫。然而它其實有個專門設計的用法,就是在.h中的interface中被readonly修飾的屬性,能夠在這個類的其餘類別(category)或者匿名類別中從新聲明這個屬性時,修改其讀寫限制,例如

//班級類
@interface XXClass : NSObject
@property (nonatomic, strong, readonly) NSMutableArray* students; //學生
@end
//匿名類別
@interface XXClass()
@property (nonatomic, strong, readwrite) NSMutableArray* students;
@end

這樣一來,由於匿名類別通常寫在.m文件裏(基本沒見過寫在.h文件裏的),因此外部是不能調用students屬性的setter方法,而XXClass類內部則可使用。

還有一種常見狀況是用setter和getter來模擬屬性(@property),例如:

//班級類
@interface XXClass : NSObject
@property (nonatomic, strong, readonly) NSMutableArray* students; //學生
@property (nonatomic, assign, readonly) NSUInteger studentsCount; //學生數量
@end
- (NSUInteger)studentsCount
{
    return self.students.count;
}

這裏的studentsCount是沒有內部變量的,經過getter方法僞形成屬性接口。

小結

這一篇是ObjC的接口設計模式的一部分,寫的比較詳細是幫助新手入門,給有經驗的朋友帶來一些思考,並引出接下來的內容。

相關文章
相關標籤/搜索