文章首發(fā)于個(gè)人blog
歡迎指正補(bǔ)充,可聯(lián)系lionsom_lin@qq.com
原文地址:OC中屬性和成員變量(一)概念篇
目錄
- 一券勺、什么是成員變量振惰?
- 成員變量的訪問(wèn)權(quán)限
- .h文件與.m文件中的@interface聲明成員變量的區(qū)別?
- .m文件中@implementation聲明變量?
- 二娃闲、成員變量與實(shí)例變量的區(qū)別
- 三、屬性
- 3.1匾浪、@property 歷史
- 3.2皇帮、@dynamic - 如果我們不想編譯器給我們屬性進(jìn)行自動(dòng)合成
- 3.3、如何自己重寫 setter / getter?
- 3.4蛋辈、self.myTitle 與 _myTitle的區(qū)別属拾、本質(zhì)、哪個(gè)更好冷溶?
- 四渐白、屬性的特質(zhì)
- 4.1、原子性關(guān)鍵字
- 4.2逞频、讀/寫權(quán)限
- 4.3纯衍、內(nèi)存管理
- 4.4、方法名
- 五苗胀、實(shí)際使用屬性
- 不要在init 和 dealloc 中使用 accessor 即 self.XX
一襟诸、什么是成員變量瓦堵?
成員變量的訪問(wèn)權(quán)限
- @public:在任何地方都能直接訪問(wèn)對(duì)象的成員變量
- @private:只能在 當(dāng)前類 的對(duì)象方法中直接訪問(wèn),如果子類要訪問(wèn)需要調(diào)用父類的get/set方法
- @protected:可以在 當(dāng)前類及其子類對(duì)象 方法中直接訪問(wèn),變量默認(rèn)的訪問(wèn)權(quán)限就是 protected
- @package:對(duì)于framework內(nèi)部的類是@protected的權(quán)限歌亲,對(duì)于外部的類是@private菇用,相當(dāng)于框架級(jí)的保護(hù)權(quán)限,適合使用在靜態(tài)庫(kù).a中陷揪。
場(chǎng)景設(shè)置
結(jié)論:我們?cè)?m里面聲明的變量惋鸥,子類是無(wú)法訪問(wèn)的(即使給他@public),也會(huì)被認(rèn)為是@private悍缠,所以我們的對(duì)外屬性都會(huì)放到.h去聲明卦绣,然而由于 _C 變量是 @private ,所以子類還是無(wú)法訪問(wèn)的扮休。
外部調(diào)用MidStudent
結(jié)論:由于我們沒(méi)有在Student或他的子類里面,所以只能訪問(wèn).h中@public修飾的變量,也就是name,由于age是@protrcted在外部是不能被訪問(wèn)的!
.h文件與.m文件中的@interface
聲明成員變量的區(qū)別迎卤?
OC中的分類與類擴(kuò)展
iOS中拴鸵,在類的源文件(.m)中玷坠,@interface部分的作用?
在.m中的@interface部分為 類擴(kuò)展(class extention)劲藐。
其被設(shè)計(jì)出來(lái)就是為了解決兩個(gè)問(wèn)題的八堡,
其一,定義類私有方法的地方聘芜。
其二兄渺,實(shí)現(xiàn)public readonly,private readwrite的property(意思是在h頭文件中定義一個(gè)屬性對(duì)外是readonly的,但在類的內(nèi)部希望是可讀寫的汰现,所以可以在m源文件中的@interface部分重新定義此屬性為readwrite挂谍,此時(shí)此屬性對(duì)外是只讀的,對(duì)內(nèi)是讀寫的)瞎饲。
最后口叙,若在此部分申明變量和屬性,但申明的變量嗅战,屬性和方法均為私有的妄田,只能夠被當(dāng)前類訪問(wèn),相當(dāng)于private驮捍。
.m文件中@implementation聲明變量?
- 如果沒(méi)有
{}
則為全局變量
@implementation Study_Ivar_Student
int ABC = 10;
- 如果有
{}
則為成員變量疟呐,但是為私有的
@implementation Study_Ivar_Student
{
@public
NSString * AAA;
}
@interface VS @implementation 聲明變量?
@interface中的是成員變量,子類可繼承使用东且,它的存活周期和創(chuàng)建的實(shí)體是一樣的启具,在一個(gè)控制器中,隨控制器的產(chǎn)生和銷毀而創(chuàng)建和銷毀珊泳;
@implementation下定義的是全局變量鲁冯,如果加了{}則為成員變量囤踩,但是為私有的,否則為全局變量晓褪。
二堵漱、成員變量與實(shí)例變量的區(qū)別
iOS 中成員變量、實(shí)例變量涣仿、屬性 三者區(qū)別與聯(lián)系
#import <UIKit/UIKit.h>
@interface Study_IvarVC : UIViewController
{
UIButton *yourButton;
int count;
id data;
}
@property (nonatomic, strong) UIButton *myButton;
@end
上述哪些是實(shí)例變量勤庐?哪些是成員變量?
在{ } 中所聲明的變量都為成員變量好港。 所以yourButton愉镰、count、data都是成員變量钧汹。
既然如此丈探,實(shí)例變量又是什么意思呢?
實(shí)例變量本質(zhì)上就是成員變量拔莱,只是實(shí)例是針對(duì)類而言碗降,實(shí)例是指類的聲明。{ }中的yourButton就是實(shí)例變量塘秦。id 是OC特有的類讼渊,本質(zhì)上講id等同于(void *)。所以id data屬于實(shí)例變量尊剔。
可以看到在接口 @interface 括號(hào)里面的統(tǒng)稱為”成員變量”爪幻,實(shí)例變量是成員變量中的一種!
實(shí)例變量的英文翻譯是 Instance Variable (object-specific storage)
實(shí)例的英文翻譯為Instance(manifestation of a class) 說(shuō)的是“類的表現(xiàn)”须误,說(shuō)明實(shí)例變量應(yīng)該是由類定義的變量挨稿!
除去基本數(shù)據(jù)類型int float ....等,其他類型的變量都叫做實(shí)例變量京痢。
實(shí)例變量+基本數(shù)據(jù)類型變量=成員變量
三奶甘、屬性
3.1、@property 歷史
iOS5之前历造,在蘋果使用GCC編譯器時(shí)候甩十,屬性的使用方式是:也就是手動(dòng)聲明實(shí)例變量,代碼如下:
***.h***
@interface ViewController : UIViewController
{
//屬性的實(shí)例變量
NSString *myTitle;
}
//編譯器遇到@property會(huì)自動(dòng)聲明對(duì)應(yīng)的setter/getter
@property (copy, nonatomic) NSString *myTitle;
@end
***.m***
// 編譯器遇到@synthesize會(huì)自動(dòng)實(shí)現(xiàn)setter/getter方法
// 編譯器遇到@synthesize回去訪問(wèn)myTitle的同名變量吭产,如果沒(méi)找到就報(bào)錯(cuò)侣监。
@synthesize myTitle;
iOS5之后,現(xiàn)在臣淤,蘋果將默認(rèn)編譯器從 GCC轉(zhuǎn)換為L(zhǎng)LVM(low level virtual machine)橄霉,從此 不再需要為屬性聲明實(shí)例變量 了。如果LLVM發(fā)現(xiàn)一個(gè)沒(méi)有匹配實(shí)例變量的屬性邑蒋,它將自動(dòng)創(chuàng)建一個(gè)以下劃線開(kāi)頭的實(shí)例變量姓蜂。因此按厘,在這個(gè)版本中,我們不再為輸出口聲明實(shí)例變量钱慢。
***.h***
@interface ViewController : UIViewController
// 編譯器遇到@property會(huì)自動(dòng)聲明對(duì)應(yīng)的setter/getter
@property (copy, nonatomic) NSString *myTitle;
@end
不隱藏代碼逮京,顯示全部代碼:
**.h**
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
/*
***被隱藏的代碼:***
1.這個(gè)默認(rèn)是@synthesize myTitle = _myTitle;生成的
2.所以如果我們手動(dòng)設(shè)置@synthesize myTitle,那么我們編譯器
生成的變量就是NSString *myTitle,相當(dāng)于@synthesize myTitle = myTitle,
如果設(shè)置@synthesize myTitle = youTitle束莫,那么編譯器生成的變量就是NSString *youTitle了
這要注意懒棉。
*/
NSString *_myTitle;
}
@property (copy, nonatomic) NSString *myTitle;
//***被隱藏的代碼***
//編譯器遇到@property會(huì)自動(dòng)聲明setter/getter方法
- (void)setMyTitle:(NSString *)myTitle;
- (NSString *)myTitle;
@end
**.m**
/*
***被隱藏的代碼***
1.@synthesize關(guān)鍵字會(huì)自動(dòng)實(shí)現(xiàn)setter/getter的方法
2.@synthesize myTitle = _myTitle指明了屬性myTitle的實(shí)例變量是_myTitle,setter/getter操作的對(duì)象就是_myTitle.
*/
@synthesize myTitle = _myTitle;
- (void)viewDidLoad {
[super viewDidLoad];
_myTitle = @"123";
}
//***被隱藏的代碼***
// 由關(guān)鍵字@synthesize自動(dòng)實(shí)現(xiàn)
- (NSString *)myTitle{
return _myTitle;
}
- (void)setMyTitle:(NSString *)myTitle{
_myTitle = myTitle;
}
代碼說(shuō)明:
1.編譯器遇到關(guān)鍵字@property,自動(dòng)聲明setter/getter方法览绿。
2.編譯器遇到@synthesize,自動(dòng)實(shí)現(xiàn)setter/getter方法策严。 注:Xcode 現(xiàn)在會(huì)默認(rèn)自動(dòng)添加 @synthesize 關(guān)鍵字。
3.@synthesize myTitle = _myTitle;為屬性myTitle生成了一個(gè)實(shí)例變量_myTitle饿敲,所以我們對(duì)屬性的操作self.myTitle實(shí)質(zhì)上都是在操作_myTitle變量妻导。
那么問(wèn)題來(lái)了:
1、我們能否認(rèn)為新編譯器LLVM下的@property
== 老編譯器GCC的 成員變量+ @property + @synthesize 成員變量
呢怀各?
答案是否定的
倔韭。
因?yàn)?code>成員變量+ @property + @synthesize 成員變量的形式,編譯器不會(huì)幫我們生成_成員變量
渠啤,因此不會(huì)操作_成員變量
了狐肢;
2添吗、同時(shí)@synthesize
還有一個(gè)作用沥曹,可以指定與屬性對(duì)應(yīng)的實(shí)例變量,
例如@synthesize myString = xxx;
那么self.myString
其實(shí)是操作的實(shí)例變量xxx
碟联,而非_String
了妓美。
3.2、@dynamic - 如果我們不想編譯器給我們屬性進(jìn)行自動(dòng)合成
- 第一種方法:自己實(shí)現(xiàn)存取方法
如果你只實(shí)現(xiàn)了其中一個(gè)存取方法鲤孵,那么另一個(gè)還是會(huì)由編譯器來(lái)合成壶栋。
- 第二種方法:@dynamic關(guān)鍵字
它會(huì)告訴編譯器:不要自動(dòng)創(chuàng)建實(shí)現(xiàn)屬性所用的實(shí)例變量,也不要為其創(chuàng)建存取方法普监。
并且在編譯訪問(wèn)屬性代碼的時(shí)贵试,及時(shí)沒(méi)有定義存取方法,也不會(huì)報(bào)錯(cuò)凯正,因?yàn)樗嘈胚@些方法在運(yùn)行期可以找到毙玻。
舉個(gè)例子,比方說(shuō)如果從CoreData框架中的NSManagedObject類里面繼承一個(gè)子類廊散,那么就需要在運(yùn)行期動(dòng)態(tài)創(chuàng)建存取方法桑滩。繼承NSManagedObject時(shí)之所以這么做,是因?yàn)樽宇惖哪承傩圆皇菍?shí)例變量允睹,其數(shù)據(jù)來(lái)源于后端服務(wù)器运准。代碼下:
@interface EOCPerson : NSManagedObject
@property NSString * firstName;
@property NSString * lastName;
@end
@implementation EOCPerson
@dynamic firstName , lastName;
@end
3.3幌氮、如何自己重寫 setter / getter
?
同時(shí)重寫 setter/getter
的問(wèn)題:
我們會(huì)發(fā)現(xiàn),當(dāng)我們同時(shí)重寫setter/getter
時(shí)會(huì)報(bào)錯(cuò)胁澳,為什么呢该互?這是因?yàn)楫?dāng)我們同時(shí)重寫setter/getter
時(shí),編譯器自動(dòng)添加的代碼@synthesize myTitle = _myTitle;
失效韭畸,就不會(huì)自動(dòng)為我們生成實(shí)例變量_myTitle
了慢洋,setter/getter
操作的對(duì)象就不存在了。所以我們要加上@synthesize myTitle = _myTitle;
,手動(dòng)指定setter/getter要操作的實(shí)例對(duì)象是_myTitle
陆盘。
***.h***
@interface Study_PropertyVC : UIViewController
@property (copy, nonatomic) NSString *myTitle;
// 重寫setter getter
-(NSString *)myTitle;
-(void)setMyTitle:(NSString *)myTitle;
***.m***
@implementation Study_PropertyVC
/*
@synthesize必須重寫
*/
@synthesize myTitle = _newtitle;
// 重寫setter getter
- (NSString *)myTitle{
return _newtitle;
}
- (void)setMyTitle:(NSString *)myTitle{
_newtitle = myTitle;
}
3.4普筹、self.myTitle 與 _myTitle的區(qū)別、本質(zhì)隘马、哪個(gè)更好太防?
iOS學(xué)習(xí)——屬性引用self.xx與_xx的區(qū)別
iOS之self.xxx與_xxx的區(qū)別
iOS的self.xxx和_.xxx的區(qū)別
OC中屬性self.a與_a訪問(wèn)的區(qū)別
本質(zhì)
self.name
是對(duì)屬性的訪問(wèn),是調(diào)用的name屬性
的getter/setter
方法酸员;
_.name
是對(duì)局部變量的訪問(wèn)蜒车,等價(jià)于self->_name
,并不會(huì)調(diào)用getter/setter
方法幔嗦;OC中點(diǎn)表達(dá)式(.)其實(shí)就是調(diào)用對(duì)象的setter和getter方法的一種快捷方式酿愧。
那么使用哪種方式更好呢?
推薦使用
_XX
的理由:
原因一:《Effective OC2.0》中第7條:在對(duì)象內(nèi)部盡量直接訪問(wèn)實(shí)例變量邀泉;
原因二:不進(jìn)過(guò)OC方法派發(fā)嬉挡,速度更快;
推薦使用
self.XX
的理由:
原因一:它可以兼容 懶加載汇恤;
原因二:避免了使用下劃線的時(shí)候忽視了self這個(gè)指針庞钢,例如self->_XX
;
原因三:_XX
更容易造成循環(huán)引用因谎;
原因四:使用_XX
是獲取不到父類的 屬性基括,因?yàn)樗皇菍?duì)局部變量的訪問(wèn);
原因五:self.XX
使用存在內(nèi)存管理等優(yōu)勢(shì)财岔,更加安全可靠风皿。
?綜合結(jié)論
在寫入實(shí)例變量的時(shí)候,使用
self.XX
方式匠璧,通過(guò)其 "設(shè)置方法" 來(lái)設(shè)置桐款;而在讀取實(shí)例變量時(shí)候,則使用_XX
方式患朱。此方法既能提高讀取速度鲁僚,又能保證相關(guān)屬性的"內(nèi)存管理語(yǔ)義"。
四、屬性的特質(zhì)
按照性質(zhì)可以分為四類冰沙。(原子性/內(nèi)存管理語(yǔ)義/讀寫權(quán)限/setter/getter別名)
- 原子性: nonatomic侨艾、atomic
- 讀寫權(quán)限:readwrite(讀寫)、readonly(只讀)
- 內(nèi)存管理:assign拓挥、strong唠梨、 weak、unsafe_unretained侥啤、copy
- 方法名:getter=当叭、setter=
可選值: nullable、nonnull盖灸、null_resettable蚁鳖、null_unspecified
其中atomic,nonatomic赁炎,copy在setter/getter中實(shí)現(xiàn)醉箕。
而weak和strong等則是直接作用于成員變量上。
4.1徙垫、原子性關(guān)鍵字
默認(rèn)情況下原子性是atomic
讥裤,由編譯器所合成的方法使用鎖機(jī)制保證其原子性。如果屬性具備 nonatomic
特質(zhì)姻报,則不使用同步鎖己英。
4.2燕锥、讀/寫權(quán)限
默認(rèn)是 readwrite
具備 readwrite
(讀寫)特質(zhì)的屬性擁有 setter / getter
方法苟穆。編譯器會(huì)自動(dòng)為其生成這兩個(gè)方法。
具備 readonly
(只讀)特質(zhì)的屬性僅擁有 getter
方法蜘醋。但是你可以用此特質(zhì)把某個(gè)屬性對(duì)外公開(kāi)為只讀屬性邮府,然后在實(shí)現(xiàn)文件中將其重定義為讀寫屬性荧关。(可查看《Effective Objective-C 2.0》第27條)
4.3、內(nèi)存管理
OC知識(shí)--徹底理解內(nèi)存管理(MRC褂傀、ARC)
iOS中copy,strong,retain,weak和assign的區(qū)別
iOS/OS X內(nèi)存管理(一):基本概念與原理
assign:setter 只會(huì)執(zhí)行針對(duì)簡(jiǎn)單變量,例如int加勤、float仙辟、NSInteger、CGFloat鳄梅、CGRect等叠国;
strong:此特質(zhì)表明該屬性定義了一種"擁有關(guān)系"。為這種屬性設(shè)置新值戴尸。設(shè)置方法先保留新值粟焊,并釋放舊值,然后再將新值設(shè)置上去。
weak:此特質(zhì)表明該屬性定義了一種"非擁有關(guān)系"项棠。為這種屬性設(shè)置新值時(shí)悲雳,設(shè)置方法既不保留新值,也不釋放舊值香追。此特質(zhì)同assign類似合瓢,然而在屬性所指的對(duì)象遭到摧毀時(shí),屬性值也會(huì)清空透典。
unsafe_unretained:此特質(zhì)?與assign相同晴楔,但它適用于"對(duì)象類型",該特質(zhì)表達(dá)一種"非擁有關(guān)系"峭咒,當(dāng)目標(biāo)對(duì)象遭到摧毀時(shí)税弃,屬性值不會(huì)自動(dòng)清空,這點(diǎn)與weak區(qū)別凑队。
copy:此特質(zhì)表達(dá)的所屬關(guān)系與strong類似钙皮。然而設(shè)置方法并不保留新值,而是將其拷貝顽决。
4.4短条、方法名
*** .h ***
@property (nonatomic, copy, getter=show1 ,setter=show2:) NSString * myName;
-(NSString *)show1;
-(void)show2:(NSString *)myname;
*** .m ***
@implementation XXX
@synthesize myName = _myname;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_myname = @"AAAAA";
LXLog(@"自定義屬性方法名 = %@",self.myName);
self.myName = @"DDDDD";
LXLog(@"自定義屬性方法名 = %@",self.myName);
}
// 重寫setter getter
-(NSString *)show1 {
return _myname;
}
-(void)show2:(NSString *)myName {
_myname = [myName copy];
}
輸出Log
-[Study_PropertyVC viewDidLoad] [line: 36] 自定義屬性方法名 = AAAAA
-[Study_PropertyVC viewDidLoad] [line: 39] 自定義屬性方法名 = DDDDD
五、實(shí)際使用屬性
《Effective Objective-C 2.0》中第6條才菠,第7條茸时,第18條
*** .h ***
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
// 初始化
-(id)initWithFirstName:(NSString *)firstName
lastName:(NSString *)lastName;
*** .m ***
-(id)initWithFirstName:(NSString *)firstName
lastName:(NSString *)lastName {
if (self = [super init]) {
_firstName = [firstName copy];
_lastName = [lastName copy];
}
return self;
}
注意點(diǎn)
1、應(yīng)盡量使用不可變的對(duì)象赋访,也就是說(shuō)應(yīng)該把這個(gè)類的兩個(gè)屬性都應(yīng)該設(shè)為 『只讀』可都。 (第18條)
2、由于是『只讀』蚓耽,所以不會(huì)創(chuàng)建"設(shè)置方法"渠牲,當(dāng)我們自定義方法的時(shí)候,應(yīng)該保證其屬性相關(guān)屬性聲明的特質(zhì)步悠,也就是_firstName = [firstName copy];
签杈; (第6條)
3、在對(duì)象內(nèi)部盡量直接訪問(wèn)實(shí)例變量鼎兽,這里也就是_firstName
答姥; (第7條)
5.1、不要在init 和 dealloc 中使用 accessor 即 self.XX
唐巧 - 不要在init和dealloc函數(shù)中使用accessor
正確實(shí)例
-(id)init {
self = [super init];
if (self) {
_count = [[NSNumber alloc] initWithInteger:0];
}
return self;
}
- (void)dealloc {
[_count release];
[super dealloc];
}
終結(jié)總結(jié):在init和dealloc中谚咬,一定不要使用
self.XX
方式p懈丁!
為什么呢择卦?
去 stackoverflow 上搜了一下敲长,比較不靠譜的說(shuō)法是這樣少一次函數(shù)調(diào)用郎嫁,更快。比較靠譜的說(shuō)法是:在 init 和 dealloc 中祈噪,對(duì)象的存在與否還不確定泽铛,所以給對(duì)象發(fā)消息可能不會(huì)成功。