一. 成員變量,實(shí)例變量,屬性變量
成員變量 : 用在類的內(nèi)部,無(wú)須與外部接觸.成員變量默認(rèn)是被保護(hù)的,所以不會(huì)有setter和getter方法. 成員變量是定義在{}中的變量.
實(shí)例變量: 如果變量類型是一個(gè)類. 如: UILabel * label;那么這個(gè)變量就是實(shí)例化變量. 所以實(shí)例化變量也是成員變量的一種. 不需要與外部接觸.或者稱為類的私有變量.
屬性量.
@property (nonatomic, copy) NSString *age;
屬性變量聲明之后,編譯器會(huì)自動(dòng)生成一個(gè)以下劃線開頭的實(shí)例變量_age. 不需要自己手動(dòng)再去寫實(shí)例變量. 也會(huì)自動(dòng)生成對(duì)應(yīng)的setter和getter.
二. 屬性變量的getter和setter方法
- setter: 給外部提供一個(gè)修改內(nèi)部屬性的接口,通過(guò)給對(duì)象指針發(fā)送該消息(調(diào)用setter方法)可以做到修改內(nèi)部的屬性值.
- getter: 為外部提供的一個(gè)查看內(nèi)部變量的接口.
- 舉例說(shuō)明.
UILabel * label = [[UILabel alloc] init]; [label setText:@"這是一個(gè)label"]; // 外部調(diào)用UILabel內(nèi)>部的text屬性的setter方法,修改屬性值. NSString * textStr = [label text];// 外部訪問(wèn)UILabel的getter方法,讀取該屬性的值. NSLog(@"textStr = %@",textStr); //setter方法 - (void)setAge:(NSString *)age { _age = age; } //getter方法 - (NSString *)age { return _age; } // 點(diǎn)調(diào)用 label.text = @"這是一個(gè)label"; // '.'調(diào)用在'='左邊相當(dāng)于setter textStr = label.text; // '.'調(diào)用在'='右邊相當(dāng)于getter
- 實(shí)戰(zhàn)
(1). setter: 可以添加一個(gè)規(guī)則來(lái)保證set的值是否正確等用法.// 重寫set方法,并保證該屬性的值為 >= 1 - (void)setCount:(int)count { if (count < 1) { count = 1; } _count = count; }
(2). getter : 可以精簡(jiǎn)代碼等其他好處.
聲明一個(gè)UIColor的對(duì)象屬性.每當(dāng)該類中的一個(gè)label的背景顏色改變之后,就 賦值給這個(gè)對(duì)象.那么每次都要讀取這個(gè)label的顏色屬性. 但是如果用getter方法就可以簡(jiǎn)化為- (UIColor *)color { return label.backgroundColor; }
二. 原子性修飾符 atomic / nonatomic
- atomic : 原子性. 為setter方法加鎖.線程安全,但需要消耗大量的資源. 屬性默認(rèn)為原子性atomic.
- nonatomic : 非原子性. 不為setter方法加鎖.線程不安全.適合.資源占用低.
- 在多線程中原子操作是必須的.之所以這么做就是為了保證在寫未完成的時(shí)候被另一個(gè)線程讀取.造成數(shù)據(jù)錯(cuò)誤.經(jīng)典案例: 火車票的預(yù)定和購(gòu)買. 加入atomic屬性修飾之后,setter方法就會(huì)加鎖.
{lock} if (property != newValue) { [property release]; property = [newValue retain]; } {unlock}
- nonatomic直接訪問(wèn)內(nèi)存的地址,不關(guān)心其他線程是否改變整個(gè)值,并且沒(méi)有死鎖現(xiàn)保護(hù).只需要從內(nèi)存中訪問(wèn)到當(dāng)前內(nèi)存地址中能用到的數(shù)據(jù)即可.
- 不要誤以為多線程加了atomic就是安全的. atomic只有在setter和getter的時(shí)候是原子操作.其他方面就不是atomic能管理的了. 想要安全就需要其他線程安全的操作了,比如加鎖.
三. 讀寫型修飾符
- readonly: 表明這個(gè)屬性只能讀,不能寫.系統(tǒng)只為我們生成一個(gè)getter方法下劃線開頭的成員變量.不會(huì)創(chuàng)建setter方法.
當(dāng)希望外界能讀取我們這個(gè)屬性融欧,但是不希望被外界改變的時(shí)候就用readonly敏弃。- readwrite: 表明這個(gè)屬性是可讀可寫的. 系統(tǒng)為我們這個(gè)屬性生成了setter和getter方法.
- 系統(tǒng)默認(rèn)為readwrite.
- 一般我們封裝的方法只允許外界read不允許寫. 在.h文件里用readonly修飾,在.m文件里面用readwrite修飾噪馏。這樣就可以外部只讀,內(nèi)部讀寫.
// .h文件 #import <UIKit/UIKit.h> @interface SecondViewController : UIViewController @property (nonatomic, strong, readonly) NSString * str; @end
// .m文件 #import "SecondViewController.h" @interface SecondViewController () @property (nonatomic, strong, readwrite) NSString * str; @end
四. 預(yù)備知識(shí)
內(nèi)存的棧區(qū) : 由編譯器自動(dòng)分配釋放, 存放函數(shù)的參數(shù)值, 局部變量的值等.
內(nèi)存的堆區(qū) : 一般由程序員分配釋放, 若程序員不釋放, 程序結(jié)束時(shí)可能由OS回收.
五. copy
- copy 和 mutableCopy
如果想要?jiǎng)?chuàng)建一個(gè)對(duì)象,該對(duì)象與源的內(nèi)容一致,那么可以用拷貝(copy或mutableCopy).
copy拷貝出來(lái)的對(duì)象類型總是不可變類型(例如, NSString, NSDictionary, NSArray等等)
mutableCopy拷貝出來(lái)的對(duì)象類型總是可變類型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)NSString *string = @"Jerry"; [string copy] --> 拷貝出內(nèi)容為Jerry的NSString類型的字符串 [string mutableCopy] --> 拷貝出內(nèi)容為Jerry的>NSMutableString類型的字符串 NSDictionary *dict = @{@"name" : @"Jerry"}; [dict copy] --> 拷貝出內(nèi)容與dict相同的NSDictionary類型的字典 [dict mutableCopy] --> 拷貝出內(nèi)容與dict相同的>NSMutableDictionary類型的字典 NSArray *array = @[@"Jerry"]; [array copy] --> 拷貝出內(nèi)容與array相同的NSArray類型的數(shù)組 [array mutableCopy] --> 拷貝出內(nèi)容與array相同的>NSMutableArray類型的數(shù)組
- block為什么用copy.
block是一個(gè)對(duì)象,所以block在創(chuàng)建的時(shí)候內(nèi)存是默認(rèn)在stack(棧)上的. 而不是在heap(堆)上的.所以他的作用域僅限創(chuàng)建時(shí)候的當(dāng)前上下文(函數(shù),方法等),當(dāng)在作用域外調(diào)用block就會(huì)崩潰. Copy可以將block從內(nèi)存棧區(qū)移動(dòng)到堆區(qū).這樣在作用域外也不會(huì)崩潰了. 但在ARC下, 使用copy與strong其實(shí)都一樣, 因?yàn)閎lock的retain就是用copy來(lái)實(shí)現(xiàn)的.block還是建議使用copy修飾.因?yàn)镸RC下就是就是用copy修飾的.- copy相對(duì)于直接賦值的好處.
先來(lái)看這個(gè)兩個(gè)的區(qū)別.NSArray * array; NSMutableArray * arrayM = [NSMutableArray array]; [arrayM addObject:@"A"]; array = arrayM; [arrayM addObject:@"B"]; NSLog(@"array = %@, arrayM = %@",array,arrayM); // 結(jié)果 array = ( "A", "B" ), arrayM = ( "A", "B" )
明明可變數(shù)組添加對(duì)象是在賦值之后, 為什么后面添加對(duì)象還會(huì)影響到不可變數(shù)組呢?
因?yàn)镺bjective-C支持多態(tài).所以表面上array是NSArray對(duì)象,但是其骨子里還是NSMutableArray對(duì)象.
這樣的話將會(huì)對(duì)后期DEBUG增加很大的成本, 可能會(huì)導(dǎo)致莫名其妙的錯(cuò)誤.NSArray * array; NSMutableArray * arrayM = [NSMutableArray array]; [arrayM addObject:@"A"]; array = [arrayM copy]; // 此處有不同 [arrayM addObject:@"B"]; NSLog(@"array = %@, arrayM = %@",array,arrayM); // 結(jié)果 array = ( "A" ), arrayM = ( "A", "B" )
這樣就能保證不管賦值的是可變還是不可變數(shù)組, NSArray就是NSArray了!
所以@property中NSString,NSArray,NSDictionary屬性用copy而不是strong了.
如果能夠在你的工程中正確使用copy, 將會(huì)對(duì)你的程序有不小的幫助.細(xì)節(jié)決定成敗.
- 深拷貝和淺拷貝
深拷貝(內(nèi)容拷貝): 直接拷貝整個(gè)對(duì)象內(nèi)容到另一塊內(nèi)存中.
淺拷貝(指針拷貝): 并不拷貝對(duì)象本身麦到,僅僅是拷貝指向?qū)ο蟮闹羔?指向該內(nèi)存地址.拷貝出來(lái)的對(duì)象與源對(duì)象的地址一致!這意味著修改拷貝對(duì)象的值會(huì)直接影響到源對(duì)象.如果在多層數(shù)組中,對(duì)第一層進(jìn)行內(nèi)容拷貝欠肾,其它層進(jìn)行指針拷貝瓶颠,這種情況是屬于深復(fù)制,還是淺復(fù)制刺桃?對(duì)此粹淋,蘋果官網(wǎng)文檔有這樣一句話描述:
This kind of copy is only capable of producing a one-level-deep copy. If you only need a one-level-deep copy... If you need a true deep copy, such as when you have an array of arrays...
從文中可以看出,蘋果認(rèn)為這種復(fù)制不是真正的深復(fù)制瑟慈,而是將其稱為單層深復(fù)制(one-level-deep copy)桃移。因此,有人對(duì)淺復(fù)制葛碧、完全深復(fù)制借杰、單層深復(fù)制做了概念區(qū)分。當(dāng)然进泼,這些都是概念性的東西蔗衡,沒(méi)有必要糾結(jié)于此。只要知道進(jìn)行拷貝操作時(shí)乳绕,被拷貝的是指針還是內(nèi)容即可绞惦。
5. 自定義復(fù)制
先自定義一個(gè)MyPerson的類.初始化并進(jìn)行copy或者mutableCopy會(huì)出現(xiàn)如圖問(wèn)題.找不到copyWithZone或者mutableCopyWithZone方法.
其實(shí)當(dāng)程序調(diào)用對(duì)象的copy方法來(lái)復(fù)制自身時(shí),底層需要調(diào)用copyWithZone:方法來(lái)完成實(shí)際的復(fù)制工作洋措,copy返回實(shí)際上就是copyWithZone:方法的返回值济蝉;mutableCopy與mutableCopyWithZone:方法也是同樣的道理。
那么怎么做才能讓自定義的對(duì)象進(jìn)行copy與mutableCopy呢菠发?需要做以下事情:
1.讓類實(shí)現(xiàn)NSCopying/NSMutableCopying協(xié)議王滤。
2.讓類實(shí)現(xiàn)copyWithZone:/mutableCopyWithZone:方法
該段參考:
小結(jié)iOS中的copy
iOS之對(duì)象復(fù)制
六. assign
- assign是賦值屬性.引用計(jì)數(shù)不加1.
- 一般用來(lái)修飾基礎(chǔ)數(shù)據(jù)類型(NSInteger,CGFloat等)和C數(shù)據(jù)類型(int,float,double)等.
- assign是指針賦值,不對(duì)引用計(jì)數(shù)操作雷酪,使用之后如果沒(méi)有置為nil淑仆,可能就會(huì)產(chǎn)生野指針.指向?qū)ο蟮刂返?jì)數(shù)不+1涝婉,但當(dāng)?shù)刂芬糜?jì)數(shù)為0時(shí)哥力,assign不會(huì)對(duì)地址進(jìn)行數(shù)據(jù)的抹除操作,只是進(jìn)行值釋放。這就導(dǎo)致野指針存在吩跋,即當(dāng)這塊地址還沒(méi)寫上其他值前寞射,能輸出正常值,但一旦重新寫上數(shù)據(jù)锌钮,該指針隨時(shí)可能沒(méi)有值桥温,造成奔潰。
七. weak
- 引用計(jì)數(shù)不加1.
- 當(dāng)使用weak修飾的屬性梁丘,當(dāng)對(duì)象釋放的時(shí)候侵浸,系統(tǒng)會(huì)對(duì)屬性賦值nil,objective-c有個(gè)特性就是對(duì)nil對(duì)象發(fā)送消息也就是調(diào)用方法。weak特性要求不保留傳入的對(duì)象氛谜。如果該對(duì)象被釋放掏觉,那么相應(yīng)的實(shí)例變量會(huì)被自動(dòng)賦為nil。這么做可以避免產(chǎn)生懸空指針值漫。懸空指針指向的是不再存在的對(duì)象澳腹。向懸空指針發(fā)送消息通常會(huì)導(dǎo)致程序崩潰。相應(yīng)的存方法會(huì)將傳入的對(duì)象直接賦給實(shí)例變量杨何。
- weak只能修飾對(duì)象類型.
- 用weak修飾代理屬性和用來(lái)解決循環(huán)強(qiáng)引用.
八.retain
- retain用在MRC情況下酱塔,被retain修飾的對(duì)象,引用計(jì)數(shù)retainCount要加1的危虱。
- retain只能修飾oc對(duì)象羊娃,不能修飾非oc對(duì)象,比如說(shuō)CoreFoundation對(duì)象就是C語(yǔ)言框架槽地,它沒(méi)有引用計(jì)數(shù)迁沫,也不能用retain進(jìn)行修飾。
- retain一般用來(lái)修飾非NSString 的NSObject類和其子類捌蚊。
九. strong
- 表示對(duì)對(duì)象的強(qiáng)引用.
- strong和weak默認(rèn)用strong
- retainCount + 1
- 對(duì)兩個(gè)對(duì)象之間互相強(qiáng)引用造成循環(huán)引用,內(nèi)存泄露.