iOS 屬性修飾符copy,weak,strong,assign,retain

一. 成員變量,實例變量,屬性變量

  1. 成員變量 : 用在類的內(nèi)部,無須與外部接觸.成員變量默認(rèn)是被保護的,所以不會有setter和getter方法. 成員變量是定義在{}中的變量.

  2. 實例變量: 如果變量類型是一個類. 如: UILabel * label;那么這個變量就是實例化變量. 所以實例化變量也是成員變量的一種. 不需要與外部接觸.或者稱為類的私有變量.

  3. 屬性量.@property (nonatomic, copy) NSString *age;
    屬性變量聲明之后,編譯器會自動生成一個以下劃線開頭的實例變量_age. 不需要自己手動再去寫實例變量. 也會自動生成對應(yīng)的setter和getter.

二. 屬性變量的getter和setter方法

  1. setter: 給外部提供一個修改內(nèi)部屬性的接口,通過給對象指針發(fā)送該消息(調(diào)用setter方法)可以做到修改內(nèi)部的屬性值.
  2. getter: 為外部提供的一個查看內(nèi)部變量的接口.
  3. 舉例說明.
   UILabel * label = [[UILabel alloc] init];
   [label setText:@"這是一個label"];  // 外部調(diào)用UILabel內(nèi)>部的text屬性的setter方法,修改屬性值.
   NSString * textStr = [label text];// 外部訪問UILabel的getter方法,讀取該屬性的值.
  NSLog(@"textStr = %@",textStr);
   

   //setter方法
  - (void)setAge:(NSString *)age {
       _age = age;
 }
  //getter方法
   - (NSString *)age {
       return _age;
   }
  
   // 點調(diào)用
   label.text = @"這是一個label";  // '.'調(diào)用在'='左邊相當(dāng)于setter
   textStr = label.text;          // '.'調(diào)用在'='右邊相當(dāng)于getter
  1. 實戰(zhàn)
    (1). setter: 可以添加一個規(guī)則來保證set的值是否正確等用法.
// 重寫set方法,并保證該屬性的值為 >= 1
- (void)setCount:(int)count {
   if (count < 1) {
       count = 1;
   }
   _count = count;
}

(2). getter : 可以精簡代碼等其他好處.
聲明一個UIColor的對象屬性.每當(dāng)該類中的一個label的背景顏色改變之后,就 賦值給這個對象.那么每次都要讀取這個label的顏色屬性. 但是如果用getter方法就可以簡化為

- (UIColor *)color {
   return label.backgroundColor;
}

二. 原子性修飾符 atomic / nonatomic

  1. atomic : 原子性. 為setter方法加鎖.線程安全,但需要消耗大量的資源. 屬性默認(rèn)為原子性atomic.
  2. nonatomic : 非原子性. 不為setter方法加鎖.線程不安全.適合.資源占用低.
  3. 在多線程中原子操作是必須的.之所以這么做就是為了保證在寫未完成的時候被另一個線程讀取.造成數(shù)據(jù)錯誤.經(jīng)典案例: 火車票的預(yù)定和購買. 加入atomic屬性修飾之后,setter方法就會加鎖.
{lock}
   if (property != newValue) {
        [property release];
        property = [newValue retain];
    }
{unlock}
  1. nonatomic直接訪問內(nèi)存的地址,不關(guān)心其他線程是否改變整個值,并且沒有死鎖現(xiàn)保護.只需要從內(nèi)存中訪問到當(dāng)前內(nèi)存地址中能用到的數(shù)據(jù)即可.
  2. 不要誤以為多線程加了atomic就是安全的. atomic只有在setter和getter的時候是原子操作.其他方面就不是atomic能管理的了. 想要安全就需要其他線程安全的操作了,比如加鎖.

三. 讀寫型修飾符

  1. readonly: 表明這個屬性只能讀,不能寫.系統(tǒng)只為我們生成一個getter方法下劃線開頭的成員變量.不會創(chuàng)建setter方法.
    當(dāng)希望外界能讀取我們這個屬性,但是不希望被外界改變的時候就用readonly。
  2. readwrite: 表明這個屬性是可讀可寫的. 系統(tǒng)為我們這個屬性生成了setter和getter方法.
  3. 系統(tǒng)默認(rèn)為readwrite.
  4. 一般我們封裝的方法只允許外界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ù)備知識

內(nèi)存的棧區(qū) : 由編譯器自動分配釋放, 存放函數(shù)的參數(shù)值, 局部變量的值等.

內(nèi)存的堆區(qū) : 一般由程序員分配釋放, 若程序員不釋放, 程序結(jié)束時可能由OS回收.

五. copy

  1. copy 和 mutableCopy
    如果想要創(chuàng)建一個對象,該對象與源的內(nèi)容一致,那么可以用拷貝(copy或mutableCopy).
    copy拷貝出來的對象類型總是不可變類型(例如, NSString, NSDictionary, NSArray等等)
    mutableCopy拷貝出來的對象類型總是可變類型(例如, 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ù)組
copy和mutableCopy
  1. block為什么用copy.
    block是一個對象,所以block在創(chuàng)建的時候內(nèi)存是默認(rèn)在stack(棧)上的. 而不是在heap(堆)上的.所以他的作用域僅限創(chuàng)建時候的當(dāng)前上下文(函數(shù),方法等),當(dāng)在作用域外調(diào)用block就會崩潰. Copy可以將block從內(nèi)存棧區(qū)移動到堆區(qū).這樣在作用域外也不會崩潰了. 但在ARC下, 使用copy與strong其實都一樣, 因為block的retain就是用copy來實現(xiàn)的.block還是建議使用copy修飾.因為MRC下就是就是用copy修飾的.
  2. copy相對于直接賦值的好處.
    先來看這個兩個的區(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ù)組添加對象是在賦值之后, 為什么后面添加對象還會影響到不可變數(shù)組呢?
因為Objective-C支持多態(tài).所以表面上array是NSArray對象,但是其骨子里還是NSMutableArray對象.
這樣的話將會對后期DEBUG增加很大的成本, 可能會導(dǎo)致莫名其妙的錯誤.

   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, 將會對你的程序有不小的幫助.細(xì)節(jié)決定成敗.

  1. 深拷貝和淺拷貝
    深拷貝(內(nèi)容拷貝): 直接拷貝整個對象內(nèi)容到另一塊內(nèi)存中.
    淺拷貝(指針拷貝): 并不拷貝對象本身,僅僅是拷貝指向?qū)ο蟮闹羔?指向該內(nèi)存地址.拷貝出來的對象與源對象的地址一致!這意味著修改拷貝對象的值會直接影響到源對象.

如果在多層數(shù)組中,對第一層進行內(nèi)容拷貝,其它層進行指針拷貝,這種情況是屬于深復(fù)制,還是淺復(fù)制?對此,蘋果官網(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)。因此,有人對淺復(fù)制、完全深復(fù)制、單層深復(fù)制做了概念區(qū)分。當(dāng)然,這些都是概念性的東西,沒有必要糾結(jié)于此。只要知道進行拷貝操作時,被拷貝的是指針還是內(nèi)容即可。

5. 自定義復(fù)制

先自定義一個MyPerson的類.初始化并進行copy或者mutableCopy會出現(xiàn)如圖問題.找不到copyWithZone或者mutableCopyWithZone方法.

自定義類的copy.png
其實當(dāng)程序調(diào)用對象的copy方法來復(fù)制自身時,底層需要調(diào)用copyWithZone:方法來完成實際的復(fù)制工作,copy返回實際上就是copyWithZone:方法的返回值;mutableCopy與mutableCopyWithZone:方法也是同樣的道理。
那么怎么做才能讓自定義的對象進行copy與mutableCopy呢?需要做以下事情:
1.讓類實現(xiàn)NSCopying/NSMutableCopying協(xié)議。
遵守NSCoding協(xié)議.png

2.讓類實現(xiàn)copyWithZone:/mutableCopyWithZone:方法
實現(xiàn)copyWithZero.png

該段參考:
小結(jié)iOS中的copy
iOS之對象復(fù)制

六. assign

  1. assign是賦值屬性.引用計數(shù)不加1.
  2. 一般用來修飾基礎(chǔ)數(shù)據(jù)類型(NSInteger,CGFloat等)和C數(shù)據(jù)類型(int,float,double)等.
  3. assign是指針賦值,不對引用計數(shù)操作,使用之后如果沒有置為nil,可能就會產(chǎn)生野指針.指向?qū)ο蟮刂返嫈?shù)不+1,但當(dāng)?shù)刂芬糜嫈?shù)為0時,assign不會對地址進行數(shù)據(jù)的抹除操作,只是進行值釋放。這就導(dǎo)致野指針存在,即當(dāng)這塊地址還沒寫上其他值前,能輸出正常值,但一旦重新寫上數(shù)據(jù),該指針隨時可能沒有值,造成奔潰。

七. weak

  1. 引用計數(shù)不加1.
  2. 當(dāng)使用weak修飾的屬性,當(dāng)對象釋放的時候,系統(tǒng)會對屬性賦值nil,objective-c有個特性就是對nil對象發(fā)送消息也就是調(diào)用方法。weak特性要求不保留傳入的對象。如果該對象被釋放,那么相應(yīng)的實例變量會被自動賦為nil。這么做可以避免產(chǎn)生懸空指針。懸空指針指向的是不再存在的對象。向懸空指針發(fā)送消息通常會導(dǎo)致程序崩潰。相應(yīng)的存方法會將傳入的對象直接賦給實例變量。
  3. weak只能修飾對象類型.
    weak只能修飾對象類型
  4. 用weak修飾代理屬性和用來解決循環(huán)強引用.

八.retain

  1. retain用在MRC情況下,被retain修飾的對象,引用計數(shù)retainCount要加1的。
  2. retain只能修飾oc對象,不能修飾非oc對象,比如說CoreFoundation對象就是C語言框架,它沒有引用計數(shù),也不能用retain進行修飾。
  3. retain一般用來修飾非NSString 的NSObject類和其子類。

九. strong

  1. 表示對對象的強引用.
  2. strong和weak默認(rèn)用strong
  3. retainCount + 1
  4. 對兩個對象之間互相強引用造成循環(huán)引用,內(nèi)存泄露.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容