block-對象類型的auto變量

從上些章節block-變量的捕獲(caputer)中,詳細說了基本類型的auto變量的捕獲,現在來了解下,對象類型的auto變量是怎樣捕獲和底層結構是如何的。

block自動copy的情況
在ARC環境下,編譯器會根據情況自動將棧上的block複製到堆上,比如以下情況
- block作爲函數返回值時
- block賦值給__strong指針時
- block作爲Cocoa API中方法名含有usingBlock的方法參數時
- block作爲GCD API的方法參數時


#import <Foundation/Foundation.h>
#import "RMPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...        
        RMPerson *person = [[RMPerson alloc] init];
        person.age = 20;

        void (^block)(void) = ^ {
            NSLog(@"age is %d",person.age);
        };

        [person release];
        NSLog(@"-----------");
    }
    return 0;
}

----------------- RMPerson.h -----------------
#import <Foundation/Foundation.h>
@interface RMPerson : NSObject
@property (nonatomic, assign) int age;
@end

----------------- RMPerson.m -----------------
#import "RMPerson.h"
@implementation RMPerson
- (void)dealloc {
    [super delloc];
    NSLog(@"RMPerson-delloc");
}
@end


// MRC 環境 控制檯輸出 
2018-07-03 10:34:39.523223+0800 __block的本質[20912:1898201] RMPerson-delloc
2018-07-03 10:36:17.021712+0800 __block的本質[20912:1898201] -----------
Program ended with exit code: 0

留意上面代碼,是在MRC的環境下的代碼,當NSLog(@"-----------");打印前了,RMPerson就釋放了,什麼原因呢?雖然block訪問的是對象類型的auto變量,但還是訪問了auto變量,所以block是屬於NSStackBlock,是存在棧空間的,block運行完就會釋放,它自己都不知道自己能存活多久,所以是不會作強持有RMPerson的操作。(結論一:如果block是在棧上,將不會對auto變量產生強引用)


#import <Foundation/Foundation.h>
#import "RMPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...        
        RMPerson *person = [[RMPerson alloc] init];
        person.age = 20;

        void (^block)(void) = [^ {
            NSLog(@"age is %d",person.age);
        } copy]; // copy操作,從棧中複製到堆中

        [person release];
        NSLog(@"-----------");
    }
    return 0;
}

----------------- RMPerson.h -----------------
#import <Foundation/Foundation.h>
@interface RMPerson : NSObject
@property (nonatomic, assign) int age;
@end

----------------- RMPerson.m -----------------
#import "RMPerson.h"
@implementation RMPerson
- (void)dealloc {
    [super delloc];
    NSLog(@"RMPerson-delloc");
}
@end


// MRC 環境 控制檯輸出 
2018-07-03 10:49:35.425698+0800 __block的本質[21020:1916302] -----------
Program ended with exit code: 0

從上面的控制檯輸出可以看出,即使程序結束了,RMPerson都沒有釋放,這是爲什麼呢? 因爲block做了copy操作,從棧中拷貝到了堆中,此時block強引用了RMPerson,所以保住了RMPerson。
下面我們來看一下,底層c++代碼是如何堆中的block是如何保住RMPerson的。
對象類型的auto變量

從上面源碼分析,

如果block被拷貝到對上時
  • 1.會調用block內部的copy函數
  • 2.copy函數內部會調用_Block_object_assign函數
  • 3._Block_object_assign函數會根據auto變量的修飾符(__strong、__weak、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用。
如果block從堆上移除
  • 1.會調用block內部的dispose函數
  • 2.dispose函數內部會調用_Block_object_dispose函數
  • 3._Block_object_dispose函數會自動釋放引用的auto變量(release)

block copy

總結:

當block內部訪問了對象類型的auto變量時

1.如果block是在棧上,將不會對auto變量產生強引用

2.如果block被拷貝到堆上
- 會調用block內部的copy函數
- copy函數內部會調用_Block_object_assign函數
- _Block_object_assign函數會根據auto變量的修飾符(__strong、__weak、* __unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用

3.如果block從堆上移除
- 會調用block內部的dispose函數
- dispose函數內部會調用_Block_object_dispose函數
- _Block_object_dispose函數會自動釋放引用的auto變量(release)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章