本文首發於我的個人博客:『不羈閣』 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方法
3. 點語法注意
- 點語法的本質是方法的調用,而不是訪問成員變量,當使用點語法時,編譯器會自動展開成相應的方法調用
- 如果沒有setter和getter方法,則不能使用點語法
- 不要在setter與getter方法中使用本屬性的點語法
- (void) setAge:(int)age {
// 下面的代碼會引發死循環
self.age = age;
//編譯器展開後 [self setAge:age]
}
- (int) age {
// 下面的代碼會引發死循環
return self.age;
// 編譯器展開後 [self age]
}
4. 實例變量修飾符
1. 實例變量的作用域
- @public
- 公開的
- 在有對象的前下,任何地方都可以直接訪問
- @protected
- 受保護的
- 只能在當前類和子類的對象方法中訪問
- @private
- 私有的
- 只能在當前類的對象方法中才能直接訪問
- @package
- 框架級別的
- 作用域介於私有和公開之間,只要處於同一個框架中相當於@public,在框架外部相當於@private
- 舉例:
@interface Iphone : NSObject
{
@public
int _cpu;
@private
int _size;
@protected
int _color;
@package
double _weight;
}
@end
2. 變量修飾符的繼承和在子類中的訪問
修飾符 | 類別 | 能否繼承 | 在子類中的訪問 |
---|---|---|---|
@private | 私有成員 | 能被繼承 | 不能被外部方法訪問 |
@public | 共有成員 | 能被繼承 | 不能被外部方法訪問 |
@protected | 保護成員 | 能被繼承 | 不能被外部方法訪問 |
3. 實例變量作用域使用注意事項
- 在@interface @end之間聲明的成員變量如果不做特別的說明,那麼其默認是protected的
- 一個類繼承了另一個類,那麼就擁有了父類的所有成員變量和方法,注意所有的成員變量它都擁有,只是有的它不能直接訪問。例如@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編寫步驟
- 在@inteface和@end之間寫上@property
- 在@property後面寫上需要生成getter/setter方法聲明的屬性名稱,注意因爲getter/setter方法名稱中的屬性不需要
_
,所以@property後的屬性也不需要_
。並且@property和屬性名稱之間要用空格隔開 - 在@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編寫步驟
- 在@implementation和@end之間寫上@synthesize
- 在@synthesize後面寫上和@property中一樣的屬性名稱,這樣@synthesize就會將@property生成的什麼拷貝到@implementation中
- 由於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;