iOS之copy、strong使用,block特性


身邊一同事,我印象在過去三個月,有兩次因為使用“copy”修飾UIKit控件的屬性,導致程序崩潰。他還一本正經的說我以前一直使用copy。

     好了,到這里我們就不得不說說什么時候使用copy。我的印象中,只有兩處使用了copy,即修飾NSString類型與block,其他的都是使用strong關鍵字修飾。

    說到這里,我們先來說說NSString類型,我在創建的NSString類型的屬性中,也曾也使用過strong修飾的,因為我幾乎沒有使用過NSMutableString類型轉換,我不用去考慮是用copy還是strong更好,當然為了代碼的健壯使用copy更好,以下我就作具體分析緣由。

    在MRC中,使用retain,copy進行拷貝,會使retainCount結果+1.但是如果是深拷貝,便會改變指針,retainCount = 1;下面我直接在ARC下調試,我只關心內存指針,不關心retainCount。

   

 NSString *str0 = @"a";

    NSLog(@"str0內存地址: %p",str0);    //0x107fcb088   在64位系統上得到的內存地址較短,說明存放在常量區(代碼,常量,全局,堆,棧)

    NSString *string0  = [str0 copy];

    NSLog(@"string0的內存地址: %p",string0);   //0x107fcb088  淺拷貝

   

    NSString *str = [NSString stringWithFormat:@"%@",@"a"];

    NSLog(@"str內存地址: %p",str);   //0xa000000000000611  (棧區)

    NSString *string  = [str copy];

    NSLog(@"string的內存地址: %p",string);   //0xa000000000000611  淺拷貝

    

    NSMutableString *str1 = [NSMutableString stringWithFormat:@"a"];

    NSLog(@"str1的內存地址:%p",str1);   //0x60000007c5c0

    NSMutableString *string1 = [str1 copy];

    NSLog(@"string1的內存地址: %p",string1);   //0xa000000000000611   內存地址發生了改變,進行了深拷貝,而且跟上面的地址一樣

 

總結:對於NSString類型只是引用了內存,淺拷貝;NSMutableString作為NSString的子類進行copy才是深拷貝。

     剛剛上面的深拷貝,出現跟淺拷貝一樣的地址,不由得我們需要多做兩個測試,如下:

   

 NSMutableString *strEx = [str mutableCopy];

    NSLog(@"strEx的內存地址:%p",strEx);    //0x608000263040 深拷貝

    NSMutableString *strExCopy = [str mutableCopy];

    NSLog(@"strExCopy的內存地址:%p",strExCopy); //0x60000026a440    str兩次mutableCopy的地址不一樣

     NSMutableString *stringEx = [strEx copy];

    NSLog(@"stringEx的內存地址: %p",stringEx);   //0xa000000000000611 與上面地址一樣

   NSMutableString *stringEx1 = [strExCopy copy];

    NSLog(@"stringEx1的內存地址: %p",stringEx1); //這個也是0xa000000000000611,說明兩次copy都指向同一個地址

    NSMutableString *strExEx = [strEx mutableCopy];

    NSLog(@"strExEx的內存地址:%p",strExEx);     //0x600000073d00 深拷貝

 

結論:可以看成,str1所謂的“深拷貝”,其實不是“深拷貝”,它還是拷貝了之前的地址。這樣,我得出,當進行mutable創建,其實是系統首先創建了一份NSString的地址,然后再深拷貝,相當於[NSMutableString stringWithFormat:@"a"];來自於 [str mutableCopy];。

在字符串類型NSString中使用strong還是copy,到底哪個更好,蘋果自己的API中告訴了我,copy更好,那么我們就進一步進行驗證。首先我們創建兩個字符串對象分別為strong與copy修飾的,然后再進行賦值比較,如下。

 

@property (strong,nonatomic)NSString *testStr;

@property (copy,nonatomic)NSString *testStrCopy;

- (void)viewDidLoad {

    [super viewDidLoad];

   NSString *testStr = [NSString stringWithFormat:@"%@",@"a"]; 

    NSLog(@"testStr內存地址: %p",testStr); //0xa000000000000611

    self.testStr = testStr;

    NSLog(@"self.testStr內存地址: %p",self.testStr);//0xa000000000000611   淺拷貝

    self.testStrCopy = testStr;

    NSLog(@"self.testStrCopy內存地址: %p",self.testStrCopy);//0xa000000000000611  淺拷貝

    NSMutableString *testStr1 = [NSMutableString stringWithFormat:@"%@",@"a"];

    NSLog(@"testStr1內存地址: %p",testStr);  //0x608000078a00  與上面的str1 0x60000007c5c0也不一樣,MutableCopy是重新創建了地址

    self.testStr = testStr1;

    NSLog(@"self.testStr內存地址: %p",self.testStr);//0x608000078a00 strong指向同一個地址

    self.testStrCopy = testStr1;

    NSLog(@"self.testStrCopy內存地址: %p",self.testStrCopy);//0xa000000000000611 雖然地址變了,但還是指向原來的地址

} 

 

 

總結:對於NSString類型,使用copy修飾,不會改變它原有的類型,strong會指向引用的對象,有可能改變其類型狀態,所以copy能增強NSString的健壯性----------------用下面一張圖表示

 

 

block特性

另一個使用copy的地方就是修飾block:

 

@property (nonatomic,copy)void(^demoBolck)();
@property (nonatomic,strong)void(^demoBolck1)();

int b=8;

void (^demoBolck)() = ^{

        NSLog(@"demoBolck");

    };

    NSLog(@"demoBolck %@",demoBolck);    //<__NSGlobalBlock__: 0x1085af0e0>  無論ARC還是MRC下,因不訪問外部局部(包括無外部變量或者只有全局變量),NSGlobalBlock表示在全局區

void (^demoBolck4)() = ^{

        NSLog(@"demoBolck4  %d",b);

    };

   NSLog(@"demoBolck4 %@",demoBolck4);    //<__NSGlobalBlock__: 0x10150b120>  全局區
    __block int a = 6;   //block內部引用a,並修改其值,需要用block修飾,不然可以不用。不過是引用行屬性,需要

    void (^demoBolck2)() = ^{
        NSLog(@"demoBolck2 %d",a++);
    };

    demoBolck2();

    NSLog(@"demoBolck2 %@,%d",demoBolck2,a);   //<__NSMallocBlock__: 0x600000056c50> ARC下堆區  <__NSStackBlock__: 0x7fff5d0ada28>MRC下在棧區

    NSLog(@"demoBolck2.Copy %@",[demoBolck2 copy]);    //<__NSMallocBlock__: 0x600000056c50>copy操作不管MRC或者ARC都在堆區,只是在MRC下進行copy會改變地址

    self.demoBolck = demoBolck2;

    NSLog(@"self.demoBolck %@",self.demoBolck);
    self.demoBolck1 = demoBolck2;
    self.demoBolck1();     //demoBolck2  7   能執行無問題

    NSLog(@"self.demoBolck1 %@",self.demoBolck1);     //<__NSMallocBlock__: 0x600000056c50>  strong修飾並沒有問題

__weak 本身是可以避免循環引用的問題的,但是其會導致外部對象釋放了之后,block 內部也訪問不到這個對象的問題,我們可以通過在 block 內部聲明一個 __strong 的變量來指向 weakObj,使外部對象既能在 block 內部保持住,又能避免循環引用的問題。

__block 本身無法避免循環引用的問題(__block NSObj *a = (NSObj*)b;),但是我們可以通過在 block 內部手動把 blockObj 賦值為 nil 的方式來避免循環引用的問題。另外一點就是 __block 修飾的變量在 block 內外都是唯一的,要注意這個特性可能帶來的隱患。但是__block有一點:這只是限制在ARC環境下。在非arc下,__block是可以避免引用循環的

 

 

     總結:在 Objective-C 語言中,一共有 3 種類型的 block:

  1. _NSConcreteGlobalBlock 全局的靜態 block,不會訪問外部局部變量。
  2. _NSConcreteStackBlock 保存在棧中的 block,當函數返回時會被銷毀。
  3. _NSConcreteMallocBlock 保存在堆中的 block,當引用計數為 0 時會被銷毀。

 關於block的知識,參考http://blog.devtang.com/2013/07/28/a-look-inside-blocks/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM