1. 程式人生 > >Block詳解二(底層分析)

Block詳解二(底層分析)

Block專輯:

Block講解一

MRC-block與ARC-block

Block詳解一(底層分析)

今天講述Block的最後一篇,後兩篇僅僅是加深1,2篇的理解,廢話少說,開始講解!

  • __block細節
  • __block記憶體管理
  • 迴圈引用問題

 

一:__block細節

大家可能會遇到下面的問題,block的內部想要修改外部的auto變數,但是編譯器會報問題!如下

如果block內部想要修改外部的auto變數,可以在int age 前面加入static修飾詞,變為靜態區域性變數(會一直存在記憶體中,反而不好),以及可以將int age程式碼移植到函式外面變為全域性變數! 除此之外還有沒有其他的做法了呢,顯然是有的,通過__block修飾,如下:

發現__block修改外面變數是可以達到目的的! 小結論

  1. __block可以用於解決block內部無法修飾auto變數值的問題
  2. __block不能修飾全域性變數、靜態變數(static)
  3. 編譯器會將__block變數包裝成一個物件

通過命令

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

生成main.cpp程式碼檢視原理

上面提到可以將int main函式的程式碼簡化一下,簡化成如下

首先拿到forwarding指標然後再拿到age的值

 

二:__block的記憶體管理

1. 當block在棧上時,並不會對__block變數產生強引用;

2. 當block被copy到堆時

  1. 會呼叫block內部的copy函式
  2. copy函式內部呼叫_Block_object_assign函式
  3. _Block_object_assign函式會對__block變數形成強引用(retain)

 3.當block從堆中移除時

  1. 會呼叫block內部的dispose函式
  2. dispose函式內部會呼叫_Block_object_dispose函式
  3. _Block_object_dispose函式會自動釋放引用的__block變數(release)

拓展:__block的_forwarding指標

 棧上block的forwarding指標指向堆上的block,而堆上block的forwarding指標指向自己本身的指標。

 三:迴圈引用

 關於block迴圈引用的基本概念,專輯block已經講解,本篇講述核心內容

1. 解決迴圈引用問題-ARC

(1) 用__weak、__unsafe_unretained解決

 下面用例子來鞏固下

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc]init];
        __weak Person *weakPerson = person;
        person.block = ^{
            NSLog(@"age is %d", weakPerson.age);
        };
    }
    NSLog(@"1111111");
    return 0;
}

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^ZXYBlock)(void);
@interface Person : NSObject
@property(nonatomic,copy)ZXYBlock block; @property(assign, nonatomic)int age; @end #import "Person.h" @implementation Person -(void)dealloc { NSLog(@"%s", __func__); } @end

__weak方式解決迴圈引用問題

__unsafe_unretained方式解決迴圈引用問題

 __weak,__unsafe_unretained都可以解決迴圈引用,有什麼區別呢?

結論

相同點:__weak和__unsafe_unretained都不會產生強引用

不同點:__weak指向物件銷燬時,會自動讓指標置為nil;__unsafe_unretained不安全,指向物件銷燬時,指標儲存地址         不變,如果再次訪問可能會造成野指標

 (2) 用__block方式解決

下面來探究一下為什麼__block可以解決迴圈引用?看下編譯成的c++程式碼

以前結合__block 物件變數以及__block自動變數可知:c++包含了三個物件,如下

 上面的三種關係如下,呼叫person = nil就是為了打斷其中一個迴圈引用鏈條,但是必須要呼叫block()

2. 解決迴圈引用問題-MRC

MRC下,首先要在編譯器上設定為MRC環境。Build Settings->Automatic Reference Counting設為No

(1) 用__unsafe_unretained解決

因為MRC下不存在弱指標,所以不存在__weak修飾解決迴圈引用

(2) 用__block解決

 

總結

上面是block最後一篇的講解,關於block總共有四篇部落格,應該可以講解完所有關於block的內容,應該會大大增加大家對block底層的理解,如果覺得有意義有所幫助,歡迎點贊和關注,本人會及時更新部落格!!!

&n