iOS @property 屬性相關的總結

@property 經常使用屬性

讀寫屬性:readwritereadonly
setter語意:assignretain / copy
原子性(多線程管理):atomicnonatomic
強弱引用:strongweakhtml

  • 讀寫屬性:
    readwrite :同時生成 setget 方法(默認)
    readonly :只會生成 get 方法ios

  • 控制set方法的內存管理:
    retainrelease 舊值,retain 新值。但願得到源對象的全部權時,對其餘 NSObject 和其子類(用於 OC 對象)
    copyrelease 舊值,copy 新值。但願得到源對象的副本而不改變源對象內容時(通常用於 NSStringblock )
    assign :直接賦值,不作任何內存管理(默認屬性),控制需不需生成 set 方法。對基礎數據類型 (NSIntegerCGFloat )和C數據類型(int , float , double , char , 等等),另外還有id類型segmentfault

  • 原子性(多線程管理):數組

    • atomic
      默認屬性,訪問方法都爲原子型事務訪問。鎖被加到所屬對象實例級,性能低。原子性就是說一個操做不能夠中途被 cpu 暫停而後調度, 即不能被中斷, 要不就執行完, 要不就不執行. 若是一個操做是原子性的,那麼在多線程環境下, 就不會出現變量被修改等奇怪的問題。原子操做就是不可再分的操做,在多線程程序中原子操做是一個很是重要的概念,它經常用來實現一些同步機制,同時也是一些常見的多線程 Bug 的源頭。固然,原子性的變量在執行效率上要低些。
    • nonatomic
      非原子性訪問。不加同步,儘可能避免多線程搶奪同一塊資源。是直接從內存中取數值,由於它是從內存中取得數據,它並無一個加鎖的保護來用於cpu中的寄存器計算Value,它只是單純的從內存地址中,當前的內存存儲的數據結果來進行使用。 多線程併發訪問會提升性能,但沒法保證數據同步。儘可能避免多線程搶奪同一塊資源,不然儘可能將加鎖資源搶奪的業務邏輯交給服務器處理,減小移動客戶端的壓力。
      當有多個線程須要訪問到同一個數據時,OC中,咱們可使用 @synchronized (變量)來對該變量進行加鎖(加鎖的目的經常是爲了同步或保證原子操做)。
  • 強指針(strong)、弱指針(weak)安全

    • strong
      strong 系統通常不會自動釋放,在 oc 中,對象默認爲強指針。做用域銷燬時銷燬引用。在實際開放中通常屬性對象通常 strong 來修飾(NSArrayNSDictionary),在使用懶加載定義控件的時候,通常也用strong。
    • weak
      weak 所引用對象的計數器不會加一,當對象被釋放時指針會被自動賦值爲 nil,系統會馬上釋放對象。
    • __unsafe_unretained 弱引用 當對象被釋放時指針不會被自動賦值爲 ni
      在ARC時屬性的修飾符是能夠用 assign 的(至關於 __unsafe_unretained
      在ARC時屬性的修飾符是能夠用 retain 的 (至關於 __strong)
    • 假定有N個指針指向同一個對象,若是至少有一個是強引用,這個對象只要還在做用域內就不會被釋放。相反,若是這N個指針都是弱引用,這個對象立刻就被釋放
    • 在使用 sb 或者 xib 給控件拖線的時候,爲何拖出來的先屬性都是用 weak 修飾呢?
      因爲在向 xib 或者 sb 裏面添加控件的時候,添加的子視圖是添加到了跟視圖 View 上面,而 控制器 Controller 對其根視圖 View 默認是強引用的,當咱們的子控件添加到 view 上面的時候,self.view addSubView: 這個方法會對添加的控件進行強引用,若是在用 strong 對添加的子控件進行修飾的話,至關於有兩條強指針對子控件進行強引用, 爲了不這種狀況,因此用 weak 修飾。
      注意:
      (1)addSubView 默認對其 subView 進行了強引用
      (2)在純手碼實現界面佈局時,若是經過懶加載處理界面控件,須要使用strong強指針
  • ARC管理內存是用 assign 仍是用 weak
    assign : 若是因爲某些緣由代理對象被釋放了,代理指針就變成了野指針。
    weak : 若是因爲某些緣由代理對象被釋放了,代理指針就變成了空指針,更安全(weak 不能修飾基本數據類型,只能修飾對象)。bash

weak修飾符

  • weak的做用:
    weak 關鍵字的做用弱引用,所引用對象的計數器不會加一,並在引用對象被釋放的時候自動被設置爲 nil,大大避免了野指針訪問壞內存引發崩潰的狀況,另外 weak 還能夠用於解決循環引用。服務器

  • 使用場景:
    用於一些對象相互引用的時候,避免出現強強引用,對象不能被釋放,出現內存泄露的問題。多線程

  • 實現原理:
    runtime 維護了一個 weak 表,用於存儲指向某個對象的全部 weak 指針。weak 表實際上是一個hash(哈希)表,Key 是所指對象的地址,Valueweak 指針的地址(這個地址的值是所指對象的地址)數組。(備註:strong 是經過 runtime 維護的一個自動計數表結構)
    weak 的實現原理可歸納三步:併發

    1. 初始化時:runtime 會調用 objc_initWeak 函數,初始化一個新的 weak 指針指向對象的地址。
    2. 添加引用時:objc_initWeak 函數會調用 objc_storeWeak() 函數, objc_storeWeak() 的做用是更新指針指向,建立對應的弱引用表。
    3. 釋放時,調用 clearDeallocating 函數。clearDeallocating 函數首先根據對象地址獲取全部 weak 指針地址的數組,而後遍歷這個數組把其中的數據設爲 nil ,最後把這個 entryweak 表中刪除,最後清理對象的記錄。
  • 文章推薦:
    iOS 底層解析weak的實現原理(包含weak對象的初始化,引用,釋放的分析)
    淺談iOS之weak底層實現原理app

深淺複製

深複製與淺複製

  • 深複製
    • 源對象和副本對象不一樣的各兩個對象
    • 源對象的引用計數器不變,副本對象的引用計數器爲 1(由於是新產生的)
    • 本質產生了新的對象
  • 淺複製
    • 源對象和副本對象是一個對象
    • 源對象(副本對象)引用計數器 + 1,至關於作了一次 retain 操做
    • 本質沒有產生新的對象

複製與計數器

  • 深複製
// 深複製:產生了新對象,新對象(副本對象)的計數器是1, 源對象的計數器不變
// str : 1
NSString *str = [NSString stringWithFormat:@"Tom"];
// str2 : 1
NSMutableString *str2 = [str mutableCopy];     
NSLog(@"str=%zd, str2=%zd", [str retainCount], [str2 retainCount]);
[str2 release];
複製代碼
  • 淺複製
// 淺複製:沒有產出新對象, 源對象(副本對象)的計數器會+1
NSString *str = [NSString stringWithFormat:@"Jack"];
NSString *str2 = [str copy];
[str2 release];
NSLog(@"%zd", [str retainCount]);
複製代碼

copy 與 mutableCopy

copy 產生不可變副本,mutableCopy 產生可變副本

copy與mutableCopy.png
理論上講OC中的任何一個對象均可以複製( copymutableCopy ),只是Foundation庫中的類的對象能夠直接複製,自定義的類的對象須要作一些額外的工做才能複製,但實際作app幾乎不須要複製自定義類的對象。

不可變對象和可變對象的 copymutableCopy 對比

不可變對象 copy不可變對象 是淺複製,其餘都是深複製。(不可變對象指NSArray、NSDictionary、NSString等)

可變對象/不可變對象的copy/mutableCopy.png

copy 與 strong

NSMutableArraycopystrong 修飾後的變化 把NSMutableArray用copy修飾有時就會crash,由於對這個數組進行了增刪改操做,而copy後的數組變成了不可變數組NSArray,沒有響應的增刪改方法,因此對其進行增刪改操做就會報錯。 舉例以下:

NSMutableArray *b = [NSMutableArray array];
a = b;
複製代碼
  • @property (nonatomic, copy) NSMutableArray *a;
NSMutableArray *b = [NSMutableArray array];
// a被copy後就成了NSArray了。
a = [b copy];
複製代碼
  • @property (nonatomic, strong) NSMutableArray *a; 若是是 strong,直接是賦值 a = b;右邊是什麼,左邊就是什麼,而且是強引用新值,左邊的類型會與右邊的相同,不會改變。

文章推薦

iOS 深拷貝淺拷貝與@property 引用計數關鍵字Strong,Copy,Weak,Assign
iOS 淺談:深.淺拷貝與copy.strong
數組的屬性修飾符到底用strong仍是copy?
iOS中copy和mutableCopy的詳細分析

NSString 爲何用 copy 而不用 retain

咱們經過實際操做來講明,咱們把str賦值給 zhangsanname 屬性,而後去改變 str,結果: 用 @property (nonatomic, retain) NSString *name;修飾的name答應結果爲 zhangsanabcname 屬性被修改了; 用 @property (nonatomic, copy) NSString *name;修飾的 name 答應結果爲 zhangsanname 屬性沒有被修改。

NSMutableString *str = [NSMutableString string];
str.string = @"zhangsan";
        
Person *zhangsan = [[[Person alloc] init] autorelease];
zhangsan.name = str;
        
[str appendString:@"abc"];
        
NSLog(@"%@ %@",  str, zhangsan.name);
複製代碼

下面咱們來看代碼set方法的內部實現: 當.h用@property (nonatomic, retain) NSString *name;時,_name = [name retain];至關於[name retain]_name = name;,而這兩句話至關因而先把原來的做引用計數+1,再把指針付給 _name ,實際上指向的是一塊內存,這樣會致使原來的內容改變,_name 也會改變,而實際中咱們通常不但願 _name 改變,因此咱們不用retain。

- (void)setName:(NSString *)name {
    if (_name != name) {
        [_name release];
        
        _name = [name retain];
        //_name = [name retain];至關於下邊兩句,而這兩句話至關因而先把原來的做引用計數+1,再把指針付給_name,實際上指向的是一塊內存,這樣會致使原來的內容改變,_name也會改變,而實際中咱們通常不但願_name改變,因此咱們不用retain
//        [name retain];
//        _name = name;
    }
}

- (void)dealloc {
//    [_name release];
//    _name = nil;
    self.name = nil;
    [super dealloc];
}
複製代碼

當.h用@property (nonatomic, copy) NSString *name;時,當傳入的值爲可變對象時,調用_name = [name copy]; copy 會建立一個新的對象賦值給 _name,因此 _namename 是兩塊無關的內容,改變 name 不會影響 _name

- (void)setName:(NSString *)name {
    if (_name != name) {
        [_name release];
        // 當傳入的值爲可變對象時,copy會建立一個新的對象賦值給_name,因此_name和name是兩塊無關的內容,改變name不會影響_name
        _name = [name copy];
    }
}

- (void)dealloc {
//    [_name release];
//    _name = nil;
    self.name = nil;
    [super dealloc];
}
複製代碼

@synthesize

  1. 在實現文件中使用 @synthesize propertyName,編譯器先會查找這個屬性名的setter方法和getter方法有沒有被人爲實現,若是已經實現,則再也不實現,若是沒有,則會幫咱們生成一個屬性命的setter方法和getter方法。
  2. 當在實現文件中使用了 @synthesize propertyName,編譯器還會作一件事情,在類成員變量中查找一個名爲 _propertyName 的成員變量,若是沒有,再繼續查找名爲 propertyName的成員變量,若是這兩個都沒有,編譯器會自動爲咱們生成一個私有的名爲 _propertyName 的成員變量。注意,系統自動建立的都是私有的。
  3. 當在實現文件中這樣寫 @synthesize propertyName = varName;時,settergetter 方法所對應的是一個名爲 varName 的成員變量,修改和讀取的是 varName 成員變量的值。
  4. 當咱們在實現文件中不寫 @synthesize propertyName 時,在Xcode 4.5以前的版本不會幫咱們自動實現 settergetter 方法,系統固然也再也不會爲咱們生成對應的成員變量。可是在Xcode 4.5以後能夠不用寫@synthesize了,就跟三、4同樣了。
  5. 當咱們既定義了 @synthesize,又在實現文件中人爲重寫 settergetter 方法時,那麼 @synthesize 將再也不工做,也就不會爲咱們建立沒有定義的 _propertyName 成員變量了,這時候若是在 settergetter 方法中調用 _propertyName 將會發生編譯錯誤
相關文章
相關標籤/搜索