iOS 開發:成員變量(屬性,實例變量)的相關知識

本文首發於我的個人博客:『不羈閣』 https://bujige.net
文章鏈接:https://bujige.net/blog/iOS-Member-variable.html

1. 成員變量介紹

1. 成員變量解釋

我們把Objective-C中寫在類聲明的大括號中的變量稱之爲成員變量(也稱爲屬性,實例變量)。

  • 舉例:
@interface Iphone : NSObject
{
    // 成員變量聲明
    int _cpu;            // cup   0
    int _size;     // 尺寸  0
    int _color;          // 顏色  0

    // 其中_cpu、_size、_color 就是 Iphone  類的成員變量
}

2. 成員變量特點

  • 成員變量只能通過對象來訪問
  • 成員變量不能離開類,離開類之後就不是成員變量
  • 成員變量不能再定義的同時進行初始化
  • 成員變量存儲在當前對象對應的堆的存儲空間中,不會被自動釋放,只能手動釋放
  • 成員變量前加下劃線"_"是蘋果的編程規範,或者說是程序員的習慣。這樣寫的好處在下邊會提到

2. setter和getter方法

我們無法從外界(比如其他類和文件中)直接訪問定義在類中的成員變量。爲了能夠從外界操作成員變量,我們需要爲調用者提供相應的方法來對成員變量進行訪問、賦值等操作。而定義這些方法都需要有一個有意義的名字,所以就有了getter-setter方法。

getter-setter方法格式和寫法是固定的,這也是程序員之間的一種規範,只要有人想要訪問成員變量或給成員變量賦值,就會立刻想到getter-setter方法,這樣就降低了程序員之間的溝通成本。

1. setter方法

  • 作用:用來設置成員變量,給成員變量賦值,可以在方法裏面對變量進行判斷,過濾掉一些不合理的值
  • 命名規範:
    • 必須是對象方法
    • 返回值類型爲void
    • 方法名必須以set開頭,而且後面跟上成員變量名去掉”_” ,首字母必須大寫
    • 必須提供一個參數,參數類型必須與所對應的成員變量的類型一致
    • 形參名稱和成員變量去掉下劃線相同
  • 舉例:

如:如果成員變量爲int _size 那麼與之對應seter方法聲明爲

-(void) setSize: (int) size;

  • setter方法的實現

- (void)setSize:(int)size;
{
     //成員變量以下劃線開頭的好處,就是可以區分局部變量和成員變量    
    _size = size;
}
  • setter方法的好處
    • 不讓數據暴露在外,保證了數據的安全性
    • 對設置的數據進行判斷,過濾不合理的值(比如空值、負數等等)

2. getter方法

  • 作用:爲調用者返回對象內部的成員變量的值,用來訪問成員變量
  • 命名規範:
    • 必須是對象方法
    • 必須有返回值,返回值的類型和成員變量的類型一致
    • 方法名必須是成員變量去掉下劃線
    • 一定是沒有參數的
  • 舉例

如:如果成員變量爲int _size 那麼與之對應getter方法爲

- (int) size;

  • getter方法的實現

- (int)size
{
    return _size;
}
  • getter方法的優點:

    • 可以讓我們在使用getter方法獲取數據之前,對數據進行加工
    • 比如雙十一活動,我們希望對全線商品的價格在原來的價格基礎上打五折,那麼我們只要去改成品類的價格的getter方法就可以了,讓他返回的值爲價格 * 0.5

3. getter/setter方法注意

  • 在實際的開發中,setter和getter方法不一定都會提供。如果內部的成員變量,只允許外界讀取,但是不允許修改,則通常只提供getter方法而不提供setter方法
  • 成員變量名的命名以下劃線開頭,setter和getter方法名不需要帶下劃線
  • 成員變量名使用下劃線開頭有兩個好處
    • 與getter方法的方法名區分開來
    • 可以和一些其他的局部變量區分開來,下劃線開頭的變量,通常都是類的成員變量。當我看到以下劃線開頭變量,那麼他一定是成員變量

3. 點語法

1. 點語法基本使用

如果給成員變量提供了getter和setter方法,就可以通過點語法來訪問成員變量

2. 點語法的本質

  • 其實點語法的本質就是調用了setter方法和getter方法
  • 當使用點語法時,編譯器會在程序翻譯成二進制的時候將.語法自動轉換爲setter和getter方法
  • 如果點語法在=號左邊,那麼編譯器會自動轉換爲setter方法
  • 如果點語法在=號右邊,或者沒有等號,那麼編譯器就會自動轉換爲getter方法
 
1877784-9e7e209d700578fb.png
 

3. 點語法注意

  • 點語法的本質是方法的調用,而不是訪問成員變量,當使用點語法時,編譯器會自動展開成相應的方法調用
  • 如果沒有setter和getter方法,則不能使用點語法
  • 不要在setter與getter方法中使用本屬性的點語法

- (void) setAge:(int)age {

    // 下面的代碼會引發死循環

    self.age = age;

    //編譯器展開後 [self setAge:age]

}

- (int) age {

    // 下面的代碼會引發死循環

    return self.age;

    // 編譯器展開後 [self   age]

}

4. 實例變量修飾符

1. 實例變量的作用域

 
1877784-c54d0c02201d2b15.png
 
  1. @public
    • 公開的
    • 在有對象的前下,任何地方都可以直接訪問
  2. @protected
    • 受保護的
    • 只能在當前類和子類的對象方法中訪問
  3. @private
    • 私有的
    • 只能在當前類的對象方法中才能直接訪問
  4. @package
    • 框架級別的
    • 作用域介於私有和公開之間,只要處於同一個框架中相當於@public,在框架外部相當於@private
  • 舉例:

@interface Iphone : NSObject
{
    @public
    int _cpu;
    
    @private
    int _size;
    
    @protected
    int _color;
    
     @package
    double _weight;
}
@end

2. 變量修飾符的繼承和在子類中的訪問

修飾符 類別 能否繼承 在子類中的訪問
@private 私有成員 能被繼承 不能被外部方法訪問
@public 共有成員 能被繼承 不能被外部方法訪問
@protected 保護成員 能被繼承 不能被外部方法訪問

3. 實例變量作用域使用注意事項

  1. 在@interface @end之間聲明的成員變量如果不做特別的說明,那麼其默認是protected的
  2. 一個類繼承了另一個類,那麼就擁有了父類的所有成員變量和方法,注意所有的成員變量它都擁有,只是有的它不能直接訪問。例如@private的

5. @property相關

1. 什麼是@property

  • @property是是聲明屬性的語法
  • @property用在聲明文件中告訴編譯器聲明成員變量的的訪問器(getter/setter)方法
  • 使用@property的好處是:免去我們手工書寫getter和setter方法繁瑣的代碼

2. @property基本使用

  • 在@inteface中,@property用來自動生成setter和getter的聲明

比如用@property int size;就可以代替下面的兩行聲明

- (int)size;   // getter

- (void)setSize:(int)size;  // setter

  • @property編寫步驟
    1. 在@inteface和@end之間寫上@property
    2. 在@property後面寫上需要生成getter/setter方法聲明的屬性名稱,注意因爲getter/setter方法名稱中的屬性不需要_,所以@property後的屬性也不需要_。並且@property和屬性名稱之間要用空格隔開
    3. 在@property和屬性名字之間告訴需要生成的屬性的數據類型, 注意兩邊都需要加上空格隔開

6. @synthesize相關

1. 什麼是@synthesize

  • @synthesize是實現屬性方法的語法
  • @synthesize用在實現文件中告訴編譯器實現成員變量的的訪問器(getter/setter)方法
  • 使用@synthesize好處是:免去我們手工書寫getterr和setter方法繁瑣的代碼

2. @synthesize基本使用

  • 寫在@implementation中,用來自動生成setter和getter的實現

用@synthesize size; 就可以代替

- (int)size{

}

- (void)setSize:(int)size{

}

//注意:@synthesize size; 並沒有告訴setter和getter 把size賦值給誰,返回誰

而用@synthesize size= _size;就可以代替

- (int)size{

    return _size;

}

- (void)setSize:(int)size{

    _size = size;

}

  • @synthesize編寫步驟
    1. 在@implementation和@end之間寫上@synthesize
    2. 在@synthesize後面寫上和@property中一樣的屬性名稱,這樣@synthesize就會將@property生成的什麼拷貝到@implementation中
    3. 由於getter/setter方法實現是要將傳入的形參給屬性和獲取屬性的值,所以在@synthesize的屬性後面寫上要將傳入的值賦值給誰和要返回哪個屬性的值, 並用等號連接

3. @synthesize注意點

  • @synthesize age = _age;
    • setter和getter實現中會訪問成員變量_age
    • 如果成員變量_age不存在,就會自動生成一個@private的成員變量_age
  • @synthesize age;
    • setter和getter實現中會訪問@synthesize後同名成員變量age
    • 如果成員變量age不存在,就會自動生成一個@private的成員變量age
  • 多個屬性可以通過一行@synthesize搞定,多個屬性之間用逗號連接

@synthesize age = _age, number = _number, name  = _name;

7. @property拓展

1. @property增強

  • 自Xcode4.4以後,apple對@property進行了一個增強,以後不用再寫@synthesize了,只用一個@property就可以同時生成setter/getter方法的聲明和實現
  • 如果沒有告訴@property要將傳入的參數賦值給誰,默認@property會將傳入的屬性賦值給_開頭的成員變量
用@property int size;就可以替代下面兩行聲明
- (int)size;   // getter

- (void)setSize:(int)size;  // setter

以及下面兩行實現
- (int)size{

    return _size;

}

- (void)setSize:(int)size{

    _size = size;

}


  • @property只會生成最簡單的getter和setter方法的聲明和實現,並不會對傳入的數據進行判斷
    • 如果想對傳入的數據進行過濾,那麼我們就必須重寫getter/setter方法
    • 如果不想對傳入的數據進行過濾,僅僅是提供一個方法給外界操作成員變量,那麼就可以使用@property
    • 如果重寫了setter方法,那麼property就只會生成getter方法
    • 如果重寫了getter方法,那麼property就只會生成setter方法
    • 如果同時重寫了getter/setter方法,那麼property就不會自動幫我們生成_開頭的成員變量(報錯)
  • 如果利用@property來生成getter/setter方法,那麼我們可以不寫成員變量, 系統會自動給我們生成一個_開頭的成員變量
  • 但@property自動幫我們生成的成員變量是一個私有的成員變量, 也就是說是在.m文件中生成的, 而不是在.h文件中生成的。我們在其他文件中無法查看該成員變量,但是可在本類中查看
@property int size; 
// 幫我們生成了一個_size的成員變量,而該成員變量_size是私有成員變量

2. @property修飾符

  • 多線程管理
    • atomic 默認什麼不寫就是atomic,意思是隻有一個線程訪問實例變量。效率很低
    • nonatomic 可以使用多個線程訪問實例變量。效率很快,絕大多數情況下使用nonatomic
  • 修飾是否生成getter方法的
    • readonly 只生成getter方法,不生成setter方法
    • readwrite 既生成getter,又生成setter方法(默認)

@property (nonatomic, readonly) int size;
@property (nonatomic, readwrite) int color;

  • 給所生成的getter/setter方法另起一個名稱
    • getter=你定義的getter方法名稱
    • setter=你定義的setter方法名稱(注意setter方法必須要有 :)

@property (nonatomic, getter=isMarried)  BOOL  married;
// 說明,通常BOOL類型的屬性的getter方法要以is開頭

  • 控制setter方法的內存管理

    • assign(默認):不會幫我們生成setter方法內存管理的代碼,僅僅只會生成普通的getter/setter方法,用於直接賦值,不做任何內存管理(默認,用於非OC對象類型)。默認什麼都不寫就是assign。被assign修飾的變量不是一個對象。主要用於代表簡單的數據類型,比如int、float等。
    
    @property(nonatomic, assign) int size;
    
    
    • retain:會自動幫我們生成getter/setter方法內存管理的代碼,在setter方法中,對傳入的對象進行引用計數加1的操作。retain一般用於NSObjct類以及其子類
    
    @property (nonatomic, retain) NSNumber *count;
    // 編譯器爲其生成的setter/getter方法
    
    -(NSNumber *)count {    // getter方法
        return _count;
    }
    -(void)setCount:(NSNumber *)count {    // setter
       // 1.判斷傳入的對象和當前對象是否一樣
        if (_count != count) {
            // 2.release以前的對象
            [_count release];
            // 3.retain傳入的對象
            _count = [count retain];
        }
    }
    
    
    • copy:對原有對象進行拷貝。常用於NSString類
    @property (nonatomic, copy) NSString *string;
    
    • strong:開啓ARC時才使用。強引用指針,相當於retain。默認情況下爲strong
    @property (nonatomic, strong) UIButton *btn;
    
    • weak:開啓ARC時才使用。弱引用指針,相當於assign
    @property (nonatomic, weak) UIButton *btn;
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章