block的修飾詞為什么選用copy


想必很多開發人員知道一般用copy修飾block,接下來就講解為什么需要用copy,甚至會講到其實用strong修飾block也是可以的

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

  1. _NSConcreteGlobalBlock 全局的靜態 block,沒有訪問外部局部變量(基本數據、OC對象)、成員屬性變量或只用到全局變量、靜態變量(局部或者全局靜態變量)
  2. _NSConcreteStackBlock 保存在棧中的 block,只用到外部局部變量(基本數據、OC對象)、成員屬性變量,且沒有強指針引用的block。當函數返回時會被銷毀。
  3. _NSConcreteMallocBlock 保存在堆中的 block,有強指針引用或copy修飾的成員屬性引用的block會被復制一份到堆中成為MallocBlock,沒有強指針引用即銷毀,生命周期由程序員控制。

新建一個項目,分別在ARC環境和MRC環境測試一遍

把測試文件改成MRC環境的方法:

把完整的測試代碼顯示如下:

#import "ViewController.h"

@interface ViewController ()

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

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

@end

@implementation ViewController

int b=8;//全局變量

- (void)viewDidLoad {

    [super viewDidLoad];

    void (^demoBolck)() = ^{

        NSLog(@"indemoBolck");

    }; 

    NSLog(@"demoBolck %@",demoBolck);    //<__NSGlobalBlock__: 0x1085af0e0>  無論ARC還是MRC下,因不訪問外部局部變量(基本數據、OC對象)、成員屬性變量或只用到全局變量、靜態變量(局部或者全局靜態變量),NSGlobalBlock表示在全局區

    void (^demoBolck4)() = ^{

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

    };

    NSLog(@"demoBolck4 %@",demoBolck4);    //<__NSGlobalBlock__: 0x10150b120>  全局區

    __block int a = 6;   //block內部引用a,並修改其值,需要用block修飾,不然可以不用

    void (^demoBolck2)() = ^{

        NSLog(@"indemoBolck2 %d",a++);

    };

    demoBolck2();

    NSLog(@"demoBolck2 %@,%d",demoBolck2,a);   //<__NSMallocBlock__: 0x600000056c50> ARC下堆區,在ARC模式下,系統也會默認對Block進行copy操作,Block的內存地址這時候便顯示在堆區; <__NSStackBlock__: 0x7fff5d0ada28>MRC下在棧區

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

    self.demoBolck = demoBolck2;

    NSLog(@"self.demoBolck %@",self.demoBolck);//堆區<__NSMallocBlock__: 0x608000052630>

    self.demoBolck1 = demoBolck2;

    self.demoBolck1();     //demoBolck2  7   能執行無問題

    NSLog(@"self.demoBolck1 %@",self.demoBolck1);     //<__NSMallocBlock__: 0x600000056c50>  strong修飾ARC和MRC都並沒有問題, 但是assign和retain在MRC環境下是還是在棧區的,會有問題

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    //注意:MRC環境下:demoBolck1用assign,retain修飾,棧區:<__NSStackBlock__: 0x7fff50915a50>,提前釋放了所以運行到下面語句程序會崩潰。ARC環境下:用copy,strong, assign, retain修飾是可以正常打印出結果的。 無論什么環境,用copy,strong修飾是可以正常打印出結果的

    self.demoBolck1();

}

@end

總結:

  • block內部沒有調用外部局部變量時存放在全局區(ARC和MRC下均是)
  • block使用了外部局部變量,這種情況也正是我們平時所常用的方式。MRC:Block的內存地址顯示在棧區,棧區的特點就是創建的對象隨時可能被銷毀,一旦被銷毀后續再次調用空對象就可能會造成程序崩潰,在對block進行copy后,block存放在堆區.所以在使用Block屬性時使用copy修飾。但是ARC中的Block都會在堆上的,系統會默認對Block進行copy操作
  • 用copy,strong修飾block在ARC和MRC都是可以的,都是在堆區

 

補充:一個block要使用self,會處理成在外部聲明一個weak變量指向self,然而為何有時會出現在block里又聲明一個strong變量指向weakSelf?
原因:block會把寫在block里的變量copy一份,如果直接在block里使用self,(self對變量默認是強引用)self對block持有,block對self持有,導致循環引用,所以這里需要聲明一個弱引用weakSelf,讓block引用weakSelf,打破循環引用。
而這樣會導致另外一個問題,因為weakSelf是對self的弱引用,如果這個時候控制器pop或者其他的方式引用計數為0,就會釋放,如果這個block是異步調用而且調用的時候self已經釋放了,這個時候weakSelf已就變成了nil。
當控制器(也可以是其他的控件)pop回來之后(或者一些其他的原因導致釋放),網絡請求完成,如果這個時候需要控制器做出反映,需要strongSelf再對weakSelf強引用一下。
但是,你可能會疑問,strongSelf對weakSelf強引用,weakSelf對self弱引用,最終不也是對self進行了強引用,會導致循環引用嗎。不會的,因為strongSelf是在block里面聲明的一個指針,當block執行完畢后,strongSelf會釋放,這個時候將不再強引用weakSelf,所以self會正確的釋放。

 


免責聲明!

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



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