iOS OC語言: Block底層實現原理

來源:互聯網
上載者:User

標籤:

來源http://www.wtoutiao.com/p/11dgbk4.html

先來簡單介紹一下Block

Block是什嗎?

蘋果推薦的類型,效率高,在運行中儲存代碼。用來封裝和儲存代碼,有點像函數,Block可以在任何時候執行。

Block和函數的相似性:(1)可以儲存代碼(2)有傳回值(3)有形參(4)調用方式一樣。

Block 底層實現

定義一個簡單的block

我們再給a賦值為20,此時列印出來a 的值還是10

但當我們在第一次給a 賦值時,前面加上__block 的時候,則列印出來20。

那麼為什麼加上__block 後 就列印出20了呢,這個原理是什麼呢?

其實可以用兩個詞來概括:傳值 和傳址。 可能這樣說大家覺得有點扯,接下來 用C++ 代碼進行編譯。

開啟終端做如下操作 在當前檔案夾下會得到一個.cpp 檔案。

此時開啟當前的.cpp 檔案(會有差不多10萬行代碼),前面我們都忽略,只需要滾動到最後,此時你會發現block跟OC中的變化。

接下來我們一個個來看這個block,先來看等號左邊的。

 void(*block)()

這是一個沒有參數沒有傳回值的函數指標,既然是一個函數指標,那它就是一個變數,變數裡面只能儲存函數地址,然後它又在等號的左邊是不是意味著右邊返回的是一個函數地址(自己推斷)。

再看等號右邊:

((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
  • 參數(自我推斷):

((void (*)()) 強轉(自己理解其實沒有實際含義,不影響自己本身的類型)

& 取址 後面都是函數的調用,如果不是也不會得到一個函數指標的。

__main_block_impl_0 這是一個函數名,這個函數有三個參數, com+F 搜尋一下,又會發現這是一個結構體,結構體如下:

      struct __main_block_impl_0 {          struct __block_impl impl;          struct __main_block_desc_0* Desc;          int a;

可能你會疑惑,剛剛說這是一個函數,而現在是一個結構體。其實在 c++ 裡面結構體相當於OC的類,c++ 裡面結構體擁有自己的屬性以及構造方法和方法。那麼為什麼取一個結構體的地址呢? 其實它取得是下面這段代碼的地址:

    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {        impl.isa = &_NSConcreteStackBlock;        impl.Flags = flags;        impl.FuncPtr = fp;        Desc = desc;    }

那麼在上面個方法實現裡,又有四個參數。而在剛剛調用的時候只有三個參數,多了一個參數 flags= 0,這個參數其實就相當於Swift中指定了一個預設值,不傳也有值,可以忽略。那麼後面繼續:

a(_a) : 在 c++ 裡面 指定_a(形參) 將來賦值給a 這個實參,也就是這個__main_block_impl_0 結構體中的 int a;在這裡 int a = 10;

impl.FuncPtr = fp; 將fp賦值給了 impl 結構體的 FuncPtr 參數, 在這個參數裡面存放的是下面這段代碼的地址:

    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {        int a = __cself->a; // 這裡 int a = 10;        printf("%d\\\\\\\\n",a); // 列印出a    }    __main_block_desc_0_DATA com+ F 搜尋 定義的就是與大小相關的資訊,代碼如下:    static struct __main_block_desc_0 {        size_t reserved;        size_t Block_size;    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

a 直接放a 其實就相當於把a 當前的值拿過來,如果是&a, 就是a的地址。請看:

接下來,又重新給 a賦值為 20,但是Block 最終要找到 FuncPtr 裡面存放的是值來執行, 在這裡才會最終執行列印a 的值的代碼,但是這段代碼裡 a 是 10 了。所以最終列印的還是10。

最後可以概括為block 底層實現 分兩種:剛剛上面的就是第一種(不加__block), 會建立一個結構體,實現構造方法,來接收三個參數。

接下來看加上__block 的實現。

修改我們的代碼:

再次在終端裡面進行編譯,你會發現產生的結構體會變化。

等號左邊會封裝一個__Block_byref_a_0 結構體類型的變數a,下面是結構體的聲明:

  truct __Block_byref_a_0 {    void *__isa;   //isa 類型的指標 自己的類型    __Block_byref_a_0 *__forwarding;  //與自己結構體同名,是一個自己類型的結構體的指標,存放的是自己的地址    int __flags;  // 標記    int __size;  // 類型的大小    int a;  // a 屬性 儲存變數的值  };

等號右邊:

  {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
  • 參數:

      (void*)0 : 一個指標直接存到isa裡面  (__Block_byref_a_0 *)&a: 強轉 存放的是自己的地址  0 : 會傳給 flags  sizeof(__Block_byref_a_0), 10: 類型的大小  10: a 的值, 僅僅是建立。

這 裡僅僅是建立,因為使用了__block 所以建立了一個block 類型的結構體,接下來會才是調用block,你會發現其餘參數和第一種實現都一樣,唯一不同的是再去取值的時候,拿到的是結構體的地址,只要把地址傳遞過 去,就有了最高的操作許可權,到時候再去取值就可以取到記憶體中最新的值。

接下來(a.__forwarding->a) = 20; 這句代碼是拿到結構體裡面的地址去修改a的值為20。

後面再去列印,列印的就是記憶體位址中最新的值,所以就是20。

Liwjing地址:http://www.jianshu.com/users/8df89a9d8380/latest_articles

iOS OC語言: Block底層實現原理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.