Objective - C block(三)block的copy、weak、__block

(一)copy

ARC環(huán)境下,編譯器會(huì)根據(jù)情況自動(dòng)將stackblock進(jìn)行copy操作,復(fù)制到堆上

  1. block作為函數(shù)返回值時(shí)
  2. 將block賦值給__strong指針時(shí)
  3. block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時(shí)
  4. block作為GCD API的方法參數(shù)時(shí)
  //1 2 示例省略
  //block作為GCD API的方法參數(shù)時(shí)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    ZQPerson *person = [[ZQPerson alloc] init];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"---------- %@",person);
    });
}

  //block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時(shí)
  NSArray *array = @[@(5), @(8), @(10)];
  [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    NSLog(@"%d %@",idx,obj);
  }];
(1)block屬性的常用寫法(copy strong進(jìn)行強(qiáng)引用)
  1. MRC下block屬性的建議寫法
    @property (copy, nonatomic) void (^block)(void);

  2. ARC下block屬性的建議寫法
    @property (strong, nonatomic) void (^block)(void);
    @property (copy, nonatomic) void (^block)(void);

(2) block內(nèi)部訪問對象類型的auto變量
  1. block是在棧上(stackBlock),將不會(huì)對auto變量產(chǎn)生強(qiáng)引用

  2. block被拷貝到堆上

  • 會(huì)調(diào)用block內(nèi)部的copy函數(shù)
  • copy函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
  • _Block_object_assign函數(shù)會(huì)根據(jù)auto變量的修飾符(__strong、__weak、__unsafe_unretained)做出相應(yīng)的操作,形成強(qiáng)引用(retain)或者弱引用
  1. 如果block從堆上移除
  • 會(huì)調(diào)用block內(nèi)部的dispose函數(shù)
  • dispose函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
  • _Block_object_dispose函數(shù)會(huì)自動(dòng)釋放引用的auto變量(類似release,斷開block對變量的持有)
block內(nèi)部訪問對象類型的auto類型的變量①

block內(nèi)部訪問對象類型的auto類型的變量②

(二)weak

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    ZQPerson *person = [[ZQPerson alloc] init];
    __weak ZQPerson *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"---------- %@",weakPerson);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"---------- %@",person);
        });
    });
}

//輸出結(jié)果:
/*block對weakPerson弱引用,但是因?yàn)楹竺娴膒erson變量被強(qiáng)引用,所以person還沒有被釋放
---------- <ZQPerson: 0x60000189c7b0>
---------- <ZQPerson: 0x60000189c7b0>
dealloc
*/
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    ZQPerson *person = [[ZQPerson alloc] init];
    __weak ZQPerson *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"---------- %@",person);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"---------- %@",weakPerson);
        });
    });
}
//輸出結(jié)果:
/*person變量被強(qiáng)引用,后面的block對weakPerson弱引用,此時(shí)person已被釋放
---------- <ZQPerson: 0x6000005c3eb0>
dealloc
---------- (null)
*/

通過上面的兩個(gè)代碼。我們可以看到結(jié)果不同

在使用clang轉(zhuǎn)換OC為C++代碼時(shí),可能會(huì)遇到以下問題cannot create __weak reference in file using manual reference
解決方案:支持ARC、指定運(yùn)行時(shí)系統(tǒng)版本,比如:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-9.0.0 main.m

(三)__block

(1)block修改變量
  • 在block內(nèi)部修改auto類型的變量是報(bào)錯(cuò)的
  • 在block內(nèi)部可以修改static類型的變量,但是永久在內(nèi)存中
  • 在block內(nèi)部可以修改全局變量


    image.png

如何將在block內(nèi)部修改auto類型的變量呢?__block

  __block int age = 10;
  ZQBlock block = ^{
    age = 20;
    NSLog(@"%d",age);
  };
  block();
(2)__block的本質(zhì)
  • __block可以用于解決block內(nèi)部無法修改auto變量值的問題
  • __block不能修飾全局變量、靜態(tài)變量(static)
  • 編譯器會(huì)將__block變量包裝成一個(gè)對象
    __block的底層結(jié)構(gòu)

    改值
疑問(1):如果auto變量是對象?
如果是對象,則會(huì)多兩個(gè)函數(shù)
疑問(2):如果block內(nèi)部操作對象屬性/方法?
  //不需要使用__block 能不加就不加 因?yàn)闀?huì)被多包裝一層
  NSMutableArray *array = [NSMutableArray array];
  ZQBlock block = ^{
    [array addObject:@"123"];
  };
  block();
疑問(3):如下面的代碼,block調(diào)用完后,繼續(xù)訪問age、person變量,他們的地址存放在哪里?
  __block int age = 10;
  __block ZQPerson *person = [[ZQPerson alloc] init];
        
  NSLog(@"1 %d %p %@",age,&age, person);
        
  ZQBlock block = ^{
    age = 20;
    person = [[ZQPerson alloc] init];
            
    NSLog(@"2 %d %p %@",age,&age, person);
  };
  block();
        
  NSLog(@"3 %d %p %@",age,&age, person);

/*輸出結(jié)果:
1 10 0x7ffeefbff558 <ZQPerson: 0x100508850>
2 20 0x1005093b8 <ZQPerson: 0x1005093f0>
3 20 0x1005093b8 <ZQPerson: 0x1005093f0>
*/

答:
訪問的age和person其實(shí)是block創(chuàng)建的對象中的age與person
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。