iOS中block的一些總結(jié)以及__block是怎么實現(xiàn)的

#Block

Block概述

Block它是C語言級別和運行時方面的一個特征敏弃。Block封裝了一段代碼邏輯,也用{}括起,和標準C語言中的函數(shù)/函數(shù)指針很相似,此外就是blokc能夠?qū)Χx環(huán)境中的變量可以引用到聊训。這一點和其它各種語言中所說的“閉包”是非常類似的概念。在iOS中恢氯,block有很多應用場景魔眨,比如對代碼封裝作為參數(shù)傳遞。這在使用dispatch并發(fā)(Operation中也有BlockOperation)和completion異步回調(diào)等處都廣泛應用酿雪。

  • Block是蘋果官方特別推薦使用的數(shù)據(jù)類型,使用場景比較廣泛
  • 動畫
  • 多線程
  • 集合遍歷
  • 網(wǎng)絡請求回調(diào)
  • Block的作用
  • 用來保存某一段代碼侄刽,可以在恰當時候再去出來調(diào)用
  • 功能類似于函數(shù)和方法
  1. Block的基本使用
聲明:(返回類型)(^聲明的block名稱)(參數(shù)列表);

實現(xiàn):^(參數(shù)列表){代碼塊}

返回值類型 (^block變量名)(參數(shù)列表) = ^(形參列表) {
}指黎;

賦值的例子:

int sum(int value1, int value2)
{
    return value1 + value2;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int (*sumP)(int, int);
        sumP = sum;
        NSLog(@"sum = %i",sumP(10,20));
        int (^sumBlock) (int value1, int value2) = ^(int value1,int value2) {
            return value2 + value1;
        };
        
        NSLog(@"sum----%i",sumBlock(10,30));
    }
    return 0;
}

其實Block和C語言中的指向函數(shù)的指針差不多
其實我們完全可以定義C語言的函數(shù),如下所示

int sum(int value1, int value2)
{
    return value1 + value2;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int (*sumP)(int, int);
        sumP = sum;
        NSLog(@"sum = %i",sumP(10,20));
        
    }
    return 0;
}

有時為了方便州丹,用typedef給出定義醋安,在蘋果的官方文檔里,建議出現(xiàn)多次的block使用typedef定義墓毒。

而block的調(diào)用吓揪,則十分簡單:block名稱(參數(shù)列表);

  1. 使用中的注意點
  • block中可以定義和外界同名的變量,并且如果在block中定義了和外界同名的變量所计,在block中訪問的是block內(nèi)部的變量
  • 默認情況下柠辞,在block內(nèi)部不能改變外面變量的值
  • 因為block中變量和外面的變量并不是同一個變量,可以通過打打印變量的地址看出來
  • 如果block中訪問到了外界的變量主胧,block會將外界變量拷貝一份到堆內(nèi)存中
  • 因為block中使用的外面變量的值是拷貝過來的即值拷貝叭首,所以在調(diào)用之前修改外界變量的值,不會影響到block中拷貝的值
  • 如果想在block中修改外界變量的值踪栋,必須在外界變量前面加上__block
  • 如果在block中修改了外界變量的值焙格,會影響到外界變量的值,

我們可以把.m反編譯成c++文件夷都,看看其內(nèi)部是怎么實現(xiàn)__block這個關(guān)鍵詞可以修改外部變量的

int sum(int value1, int value2)
{
    return value1 + value2;
}

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 int __main_block_func_0(struct __main_block_impl_0 *__cself, int value1, int value2) {

            return value2 + value1;
        }

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, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int (*sumP)(int, int);
        sumP = sum;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_185299_mi_0,sumP(10,20));
        int (*sumBlock) (int value1, int value2) = ((int (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_185299_mi_1,((int (*)(__block_impl *, int, int))((__block_impl *)sumBlock)->FuncPtr)((__block_impl *)sumBlock, 10, 30));
    }

    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

加上__block

int sum(int value1, int value2)
{
    return value1 + value2;
}

struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int value1, int value2) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref

            (a->__forwarding->a) = 20;
            return value2 + value1;
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int (*sumP)(int, int);
        sumP = sum;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_9f1f9e_mi_0,sumP(10,20));
        __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
        int (*sumBlock) (int value1, int value2) = ((int (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_9f1f9e_mi_1,((int (*)(__block_impl *, int, int))((__block_impl *)sumBlock)->FuncPtr)((__block_impl *)sumBlock, 10, 30));
    }

    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

很明顯加上了__block之后傳入的是&這樣的眷唉,是把地址傳到block內(nèi)部去了,所以可以修改外部變量了

  • block是存儲在堆中還是棧中呢
  • 默認情況下block是存儲在棧中的,如果對block進行一個copy操作冬阳,block會轉(zhuǎn)移到堆中
  • 如果block在棧中 蛤虐,block中訪問了外界的對象,那么不會對對象進行retain操作
  • 但是如果block在堆中摩泪,block中訪問了外面的對象笆焰,那么會對外界的對象進行一次retain操作
  • 如果block中訪問了外界的對象,一定要給對象加上__block见坑,只要加上__block嚷掠,哪怕block在堆中,也不會對外界的對象進行retain
  • 想把block弄到堆中可以使用Clock_copy()

有如下一些注意點:

對定義環(huán)境的變量使用荞驴。默認是以const的方式使用不皆,這有點像函數(shù)的const參數(shù)傳遞,如過需要block內(nèi)修改可變熊楼,則使用__block霹娄,這樣做存儲就實現(xiàn)了共享,包括塊中的遞歸應用和定義環(huán)境上下文中的多個block使用鲫骗。block通常定義在棧幀當中犬耻,而當所處的棧幀被銷毀的時候,block以及引用到的__block變量將會依然有效执泰。

引用類型問題枕磁。block中的引用默認都是強引用,必要的時候需要使用__weak术吝,同delegate使用的注意一樣计济,避免循環(huán)引用。此外排苍,蘋果文檔中還給出了對instanceVariable和對localVariable引用不同的例子沦寂,注意體會下。

copy淘衙。在類屬性中传藏,要使用copy。此外彤守,對block進行copy要使用Block_copy()/Block_release()漩氨。

蘋果文檔中幾種需要避免使用的方式∫旁觯《Blocks Programming Topics》中Using Blocks中的例子叫惊,實際上就是要注意block定義的位置與其上下文的關(guān)系。

有關(guān)Block的地址/引用做修。注意這篇文章中最后例子中的問題:http://www.cnblogs.com/kesalin/archive/2013/04/30/ios_block.html我的理解是Block的地址發(fā)生了變化霍狰,最終的問題是對Block引用地址釋放時的野指針錯誤抡草。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蔗坯,隨后出現(xiàn)的幾起案子康震,更是在濱河造成了極大的恐慌,老刑警劉巖宾濒,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腿短,死亡現(xiàn)場離奇詭異,居然都是意外死亡绘梦,警方通過查閱死者的電腦和手機橘忱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卸奉,“玉大人钝诚,你說我怎么就攤上這事¢茫” “怎么了凝颇?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疹鳄。 經(jīng)常有香客問我拧略,道長,這世上最難降的妖魔是什么瘪弓? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任辑鲤,我火速辦了婚禮,結(jié)果婚禮上杠茬,老公的妹妹穿的比我還像新娘。我一直安慰自己弛随,他們只是感情好瓢喉,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舀透,像睡著了一般栓票。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上愕够,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天走贪,我揣著相機與錄音,去河邊找鬼惑芭。 笑死坠狡,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的遂跟。 我是一名探鬼主播逃沿,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼婴渡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凯亮?” 一聲冷哼從身側(cè)響起边臼,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎假消,沒想到半個月后柠并,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡富拗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年臼予,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媒峡。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘟栖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谅阿,到底是詐尸還是另有隱情半哟,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布签餐,位于F島的核電站寓涨,受9級特大地震影響氯檐,放射性物質(zhì)發(fā)生泄漏戒良。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一糯崎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧河泳,春花似錦沃呢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽流强。三九已至,卻和暖如春队腐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背第股。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工涉馅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留稚矿,地道東北人昧识。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓朵耕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親栅贴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容