日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Objective-C 之Block(2)

發(fā)布時間:2025/3/18 编程问答 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Objective-C 之Block(2) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Block的實質(zhì)

Block是“帶有自動變量值的匿名函數(shù)”。

通過clang -rewirte-objc 源代碼文件名就能將含有Block語法的源代碼變換為cpp的源代碼。

int main(int argc, char * argv[]) {void (^blk)(void) = ^{printf("Block");};blk();return 0;} 復(fù)制代碼

變換后截取其中的代碼邏輯部分如下:

struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr; };struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf("Block");}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)};int main(int argc, char * argv[]) {void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;}復(fù)制代碼

首先為:

^{printf("Block");}; 復(fù)制代碼

變換后為:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {printf("Block"); } 復(fù)制代碼

通過Block使用的匿名函數(shù)實際上被作為簡單的C語言函數(shù)來處理。 另外,根據(jù)Blocl語法所屬的函數(shù)名(此處為main)和該Block語法在函數(shù)出現(xiàn)的順序值(此處為0)來給經(jīng)clang變換的函數(shù)命名。 該函數(shù)的參數(shù)__cself相當于C++實例方法中志向?qū)嵗陨淼淖兞縯his,或者OC中的self,即參數(shù)__cself為志向Block值的變量。

這個方法中參數(shù)的聲明為:

struct __main_block_impl_0 *__cself 復(fù)制代碼

參數(shù)__cself是__main_block_impl_0結(jié)構(gòu)體的指針。

該結(jié)構(gòu)體聲明如下:

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };復(fù)制代碼

去除構(gòu)造函數(shù)的部分為:

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc; };復(fù)制代碼

再看__block_impl結(jié)構(gòu)體的聲明:

struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr; }; 復(fù)制代碼

里面包含某些標志、今后版本升級所需的區(qū)域以及函數(shù)指針。 第二個成員變量是Desc指針,以下為__main_block_desc_0結(jié)構(gòu)體的聲明。

static struct __main_block_desc_0 {size_t reserved;size_t Block_size; } 復(fù)制代碼

其結(jié)構(gòu)為今后版本升級所需要的區(qū)域和Block的大小。

再看__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù):

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} 復(fù)制代碼

以上就是初始化__main_block_impl_0結(jié)構(gòu)體成員的源代碼。

再看看main函數(shù)中構(gòu)造函數(shù)的調(diào)用如下:

void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); 復(fù)制代碼

去掉轉(zhuǎn)換部分來看如下:

__main_block_impl_0 temp = __main_block_impl_0(__main_block_func_0,&__main_block_imp_0_DATA);struct __main_block_impl_0 *blk = &temp; 復(fù)制代碼

該源代碼將__main_block_impl_0結(jié)構(gòu)體的自動變量,即棧上生成的__main_block_impl_0結(jié)構(gòu)體實例的指針,賦值給__main_block_impl_0結(jié)構(gòu)體指針類型的變量blk。

以下這句代碼代表最初的打印代碼:

void (^blk)(void) = ^{printf("Block");}; 復(fù)制代碼

將打印的Block塊賦給Block類型變量blk,相當于將__main_block_impl_0結(jié)構(gòu)體的指針賦值給變量blk。打印的代碼塊就是__main_block_impl_0結(jié)構(gòu)體類型的自動變量,即棧上生成的__main_block_impl_0的結(jié)構(gòu)體實例。

再來看__main_block_impl_0實例的構(gòu)造方法參數(shù):

__main_block_impl_0(__main_block_func_0,&__main_block_imp_0_DATA); 復(fù)制代碼

第一個參數(shù)是由Block語法轉(zhuǎn)換C語言函數(shù)指針。第二個參數(shù)是作為靜態(tài)全局變量初始化的__main_block_desc_0結(jié)構(gòu)體實例指針。

下面是__main_block_desc_0結(jié)構(gòu)體實例的初始化部分代碼。

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)}; 復(fù)制代碼

由上部分代碼可以,該源代碼使用Block,即__main_block_impl_0結(jié)構(gòu)體實例的大小,進行初始化。

再來看__main_block_impl_0結(jié)構(gòu)體

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr; };復(fù)制代碼

以上,__main_block_impl_0結(jié)構(gòu)體等同于,結(jié)構(gòu)體構(gòu)造函數(shù)會如下進行初始化:

struct __main_block_impl_0 {void *isa;int Flags;int Reserved;void *FuncPtr;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };復(fù)制代碼

那么調(diào)用打印代碼塊的部分應(yīng)該為blk();

就可以變換為以下代碼:

((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);復(fù)制代碼

去掉轉(zhuǎn)換部分后:

(*blk->impl.FuncPtr)(blk); 復(fù)制代碼

這是簡單地使用函數(shù)指針調(diào)用函數(shù)。由打印代碼塊轉(zhuǎn)換的__main_block_func_0函數(shù)的指針被賦值到結(jié)構(gòu)體的成員變量FuncPtr中,也說明了__main_block_func_0的參數(shù)__cself指向Block值。

但是impl.isa = &_NSConcreteStackBlock;,將Block指針賦值給Block結(jié)構(gòu)體成員變量isa。

注:isa為何物?

引用簡書作者曲年_《Objective-C isa 指針 與 runtime 機制》_一文中解釋如下

在Objective-C中,任何類的定義都是對象。類和類的實例(對象)沒有任何本質(zhì)上的區(qū)別。任何對象都有isa指針。

那么什么是類呢?在xcode中用快捷鍵Shift+Cmd+O 打開文件objc.h 能看到類的定義:

#if !OBJC_TYPES_DEFINED /// An opaque type that represents an Objective-C class. typedef struct objc_class *Class;/// Represents an instance of a class. struct objc_object {Class _Nonnull isa OBJC_ISA_AVAILABILITY; };/// A pointer to an instance of a class. typedef struct objc_object *id; #endif/// An opaque type that represents a method selector. typedef struct objc_selector *SEL; 復(fù)制代碼

可以看出: Class是一個objc_class結(jié)構(gòu)類型的指針,id是一個objc_object結(jié)構(gòu)類型的指針。

objc_class結(jié)構(gòu)體的定義如下:

struct objc_class {Class _Nonnull isa OBJC_ISA_AVAILABILITY;#if !__OBJC2__Class _Nullable super_class OBJC2_UNAVAILABLE;const char * _Nonnull name OBJC2_UNAVAILABLE;long version OBJC2_UNAVAILABLE;long info OBJC2_UNAVAILABLE;long instance_size OBJC2_UNAVAILABLE;struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; #endif} OBJC2_UNAVAILABLE; /* Use `Class` instead of `struct objc_class *` */復(fù)制代碼

各參數(shù)含義如下:

  • isa:是一個Class 類型的指針. 每個實例對象有個isa的指針,他指向?qū)ο蟮念?#xff0c;而Class里也有個isa的指針, 指向meteClass(元類)。元類保存了類方法的列表。當類方法被調(diào)用時,先會從本身查找類方法的實現(xiàn),如果沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass)也是類,它也是對象。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass).根元類的isa指針指向本身,這樣形成了一個封閉的內(nèi)循環(huán)。
  • super_class:父類,如果該類已經(jīng)是最頂層的根類,那么它為NULL。
  • version:類的版本信息,默認為0
  • info:供運行期使用的一些位標識。
  • instance_size:該類的實例變量大小
  • ivars:成員變量的數(shù)組

每一個對象本質(zhì)上都是一個類的實例。其中類定義了成員變量和成員方法的列表。對象通過對象的isa指針指向類。

每一個類本質(zhì)上都是一個對象,類其實是元類(meteClass)的實例。元類定義了類方法的列表。類通過類的isa指針指向元類。

所有的元類最終繼承一個根元類,根元類isa指針指向本身,形成一個封閉的內(nèi)循環(huán)。

objc_class結(jié)構(gòu)體與objc_object結(jié)構(gòu)體相同。但是,objc_object結(jié)構(gòu)題是各個對象在實現(xiàn)中使用的最基本的結(jié)構(gòu)體,objc_class是類在視線中使用的最基本的結(jié)構(gòu)體。

例:

@interface MyObject:NSObject {int val0;int val1; } 復(fù)制代碼

基于objc_object結(jié)構(gòu)體,該類的對象的結(jié)構(gòu)體如下:

struct MyObject{Class isa;int val0;int val1; }復(fù)制代碼

MyObject類的實例變量val0和val1被直接聲明為對象的結(jié)構(gòu)體成員。 OC中由各類生成對象意味著,像該結(jié)構(gòu)體這樣“生成由該類生成的對象的結(jié)構(gòu)體實例”。生成的各個對象,即由該類生成的對象的各個結(jié)構(gòu)體實例,通過成員變量isa保持該類的結(jié)構(gòu)體實例指針。


截獲自動變量值

int main(int argc, char * argv[]) {int dmy = 256;int val = 10;const char *fmt = "val = %d\n";void (^blk)(void)=^{printf(fmt,val);};val = 2;fmt = "These values were changed.val=%d\n";blk();return 0; } 復(fù)制代碼

通過clang -rewrite-objc轉(zhuǎn)換后的代碼如下:

struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr; };struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;const char *fmt;int val;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) {const char *fmt = __cself->fmt; // bound by copyint val = __cself->val; // bound by copyprintf(fmt,val);}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)}; int main(int argc, char * argv[]) {int dmy = 256;int val = 10;const char *fmt = "val = %d\n";void (*blk)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val));val = 2;fmt = "These values were changed.val=%d\n";((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;}復(fù)制代碼

有區(qū)別的部分僅僅在于以下部分:將變量作為成員變量追加到了__main_block_impl_0 結(jié)構(gòu)體中。

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;const char *fmt;int val;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;} };復(fù)制代碼

__main_block_impl_0結(jié)構(gòu)體內(nèi)聲明的成員變量了行于自動變量類型完全相同。但是Block語法表達式中沒有使用的自動變量不會被追加。Blocks的自動變量截獲只針對Block中使用的自動變量。

在初始化結(jié)構(gòu)體實例是,根據(jù)傳遞給構(gòu)造函數(shù)的參數(shù)對由自動變量追加的成員變量進行初始化。

__main_block_impl_0初始化的代碼過程總結(jié)來說就是以下賦值過程:

impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = &__main_block_desc_0_DATA; val = 10; fmt = "val = %d\n";復(fù)制代碼

由此可知,在__main_block_impl_0結(jié)構(gòu)體實例中(即打印代碼塊),自動變量被截獲。

再來看其中匿名函數(shù)代碼塊:

^{printf(fmt,val); };復(fù)制代碼

可以轉(zhuǎn)換為:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {const char *fmt = __cself->fmt; // bound by copyint val = __cself->val; // bound by copyprintf(fmt,val);} 復(fù)制代碼

在轉(zhuǎn)換后的代碼中,截獲到__main_block_impl_0結(jié)構(gòu)體實例的成員變量上的自動變量,這些變量在Block語法表達式之前被聲明定義。因此,原來的源代碼表達式無需改動遍可以使用截獲的自動變量值執(zhí)行。

總的來說,所謂“截獲自動變量值”意味著在執(zhí)行Block語法時,Block語法表達式所使用的自動變量值被保存到Block的結(jié)構(gòu)實例中。

總結(jié)

以上是生活随笔為你收集整理的Objective-C 之Block(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。