1. 程式人生 > >OC中屬性和成員變數的區別和使用

OC中屬性和成員變數的區別和使用

    當我們在一個程式中宣告一個變數,是將它宣告為成員變數呢還是封裝成屬性呢?

    上網查了一查,再加上自己的理解,整理如下:

    首先分別介紹一下成員變數和屬性

    1) 成員變數 如下一段程式碼

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    @private
    NSString * _name;
}

@end
    成員變數宣告在 @interface 和 @end 中間,並用大括號 { } 與成員方法加以區分。

    上圖例示就是在類 Person 中聲明瞭一個字串型別成員變數 _name。

    在OC中人為規定成員變數前面新增一個下劃線 _  原因後面介紹到屬性的時候會進行闡述 (如果寫到後面還沒有忘記的話)。

    成員變數上面頂著的 @private 是對成員變數的作用域的宣告。

    @private 指的是私有成員,@private 標示的成員變數只能有類例項化的物件本身所呼叫。相對還有可以在有繼承關係的子類中訪問的 @protected 識別符號和可以任意訪問的 @public 識別符號。預設情況下(不寫作用域)是 @private。出於類的封裝等方面考慮,建議對成員變數使用 @private。

   建立的成員變數可以在類中的方法直接進行訪問,如下示例:

#import "Person.h"

@implementation Person

- (void)setPersonName {
    _name = @"張三";
}

@end
    上例是對類 Person 中的成員變數進行賦值操作。

    因為是私有的成員變數,所以沒辦法直接在類外面訪問。需要在類中定義相關方法來實現成員變數的獲取和賦值。如下例:

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    @private
    NSString * _name;
}

- (void)setName:(NSString *)name;
- (NSString *)getName;

@end
#import "Person.h"

@implementation Person

- (void)setName:(NSString *)name {
    _name = name;
}

- (NSString *)getName {
    return _name;
}

@end
#import <Foundation/Foundation.h>
#import "Person.h"

int main() {
    
    Person * p = [[Person alloc] init];
    
    [p setName:@"李四"];
    NSString * lisiName = [p getName];
    
    NSLog(@"%@", lisiName);
    
    return 0;
} 
    上例中,在 .h 檔案中聲明瞭對成員變數 _name 的賦值和獲取方法以供類外部呼叫,同時在 .m 檔案中實現相關方法。

    在main函式中例項化 Person 類,呼叫setName函式為物件 p 的成員變數 _name 賦值,然後獲取到這個值,再進行列印輸出。

    以上是成員變數的建立和使用。

    有時候我們需要在一個類中建立十多個成員變數,這就意味著需要寫雙倍的set和get方法來供外部訪問這些成員變數,而且set和get方法格式內容大都相同。

    OC中提供了一種稱為屬性的結構來簡化這一過程。

    2) 屬性

    首先來看一個例子,同樣是定義一個成員變數,我們還可以通過以下定義屬性來達到相同的目的:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property NSString * name;

@end
#import "Person.h"

@implementation Person

@synthesize name = _name;

@end
    上例中,在 .h 檔案中使用 @property 關鍵字定義了一個屬性,名字是 name 。

    系統會檢查類中是否有同名的成員變數,如果沒有,則會隱式的幫助宣告一個成員變數 _name 。 這一點最直觀的體現就是即使沒有人為宣告成員變數 _name,在類中的方法也可以賦值或者呼叫 _name。除此之外,系統還會在 .h 檔案中為類隱式的宣告get和set方法。set 方法的格式就是在首字母大寫的屬性名前面加上 set ,get 方法直接以屬性名來命名。

    而在 .m 檔案中,可以使用關鍵字 @synthesize 來對屬性進行繫結。@synthesize 關鍵字後面緊跟著的是屬性名字,等號後面即為該屬性所繫結的成員變數。該關鍵字的作用是在 .m 中對set和get方法進行實現。具體實現格式與上面格式的例子差不多(不考慮記憶體管理的時候)

    在較新的版本中,關鍵字 @synthesize 可以省略,僅使用 @property 關鍵字就可以完成成員變數和 set get 方法對建立。

    在呼叫方面,使用屬性定義的成員變數,可以使用 set get 方法呼叫,也可以使用點語法呼叫。因為屬性隱式生成的成員變數是@private型別的,所以並不能直接使用->來呼叫。示例如下:

#import <Foundation/Foundation.h>
#import "Person.h"

int main() {
    
    Person * p = [[Person alloc] init];
    
    [p setName:@"李四"];
    
    NSLog(@"%@", [p name]);
    
    return 0;
}
    實現結果和上一個是一樣一樣的。

    從上面的比較來看,省時省力省行數的屬性似乎應該更有使用價值。但是我們依然可以通過手動來定義成員變數。所謂萬事皆有因。下面來說說成員變數的屬性的使用場合。

    定義屬性的目的是為了對外部提供一個訪問類內部私有成員變數的介面。這就意味著這些定義的屬性對外部是敞開的,當然我們也可以通過對屬性進行設定來使其只提供 get 方法等確保資料安全穩定。但是,當我們希望僅僅在這個類中的不同方法中共享一個變數而不希望對被外部所看到時候,定義屬性未免太過浪費和不合適。這時候就體現到成員變數的優勢。而在 .m 中也可以定義成員變數更是極大避免了程式碼正碼到激情四射時候需要切換檔案的不爽。

 ----------

    綜上所述,屬性是在希望展示給類外部的類本身屬性時候建立,成員變數則是在類內部需要共享訪問時候建立。

    以上內容僅代表個人觀點。如有錯誤歡迎指正。