重點 (二十七) : 深究Block

總結(jié):

block本質(zhì)是指向一個結(jié)構(gòu)體的一個指針運行時機制 比較高級的特性 純C語言,平時寫的OC代碼 轉(zhuǎn)換成C語言運行時的代碼指令:clang
-rewrite-objc main.m(可以打印驗證),默認情況下,任何block都是在棧里面的,隨時可能被回收只要對其做一次copy操作 block的內(nèi)存就會放在堆里面不會釋放,只有copy才能產(chǎn)生一個新的內(nèi)存地址所有地址會發(fā)生改變

【手繪block詳解】

1.png

2.png

3.png

*************************筆記**************************


本文翻譯自蘋果的文檔,有刪減,也有添加自己的理解部分。

如果有Block語法不懂的,可以參考fuckingblocksyntax,里面對于Block

為了方便對比,下面的代碼我假設(shè)是寫在ViewController子類中的

1、第一部分

定義和使用Block,

  • (void)viewDidLoad

{

[super viewDidLoad];

(1)定義無參無返回值的Block

void (^printBlock)() = ^(){

printf("no number");

};

printBlock();

printBlock(9);

int mutiplier = 7;

(3)定義名為myBlock的代碼塊,返回值類型為int

int (^myBlock)(int)
= ^(int num){

return num*mutiplier;

}

 使用定義的myBlock

int newMutiplier = myBlock(3);

printf("newMutiplier is %d",myBlock(3));

}

定義在-viewDidLoad方法外部

(2)定義一個有參數(shù),沒有返回值的Block

void (^printNumBlock)(int)
= ^(int num){

printf("int

number is %d",num);

};

定義Block變量,就相當于定義了一個函數(shù)。但是區(qū)別也很明顯,因為函數(shù)肯定是在-viewDidLoad方法外面定義,而Block變量定義在了viewDidLoad方法內(nèi)部。當然,我們也可以把Block定義在-viewDidLoad方法外部,例如上面的代碼塊printNumBlock的定義,就在-viewDidLoad外面。

再來看看上面代碼運行的順序問題,以第(3)個myBlock距離來說,在定義的地方,并不會執(zhí)行Block{}內(nèi)部的代碼,而在myBlock(3)調(diào)用之后才會執(zhí)行其中的代碼,這跟函數(shù)的理解其實差不多,就是只要在調(diào)用Block(函數(shù))的時候才會執(zhí)行Block體內(nèi)(函數(shù)體內(nèi))的代碼。所以上面的簡單代碼示例,我可以作出如下的結(jié)論,

(1)在類中,定義一個Block變量,就像定義一個函數(shù);

(2)Block可以定義在方法內(nèi)部,也可以定義在方法外部;

(3)只有調(diào)用Block時候,才會執(zhí)行其{}體內(nèi)的代碼;

(PS:關(guān)于第(2)條,定義在方法外部的Block,其實就是文件級別的全局變量)

那么在類中定義一個Block,特別是在-viewDidLoad方法體內(nèi)定義一個Block到底有什么意義呢?我表示這時候只把它當做私有函數(shù)就可以了。我之前說過,Block其實就相當于代理,那么這時候我該怎樣將其與代理類比以了解呢。這時候我可以這樣說:本類中的Block就相當于類自己服從某個協(xié)議,然后讓自己代理自己去做某個事情。很拗口吧?看看下面的代碼,

定義一個協(xié)議

@protocol ViewControllerDelegate<NSObject>

  • (void)selfDelegateMethod;

@end

本類實現(xiàn)這個協(xié)議ViewControllerDelegate

@interface ViewController
()<ViewControllerDelegate>

@property (nonatomic,
assign) id<ViewControllerDelegate>
delegate;

@end

接著在-viewDidLoad中的代碼如下,

  • (void)viewDidLoad

{

[super viewDidLoad];

Do any

additional setup after loading the view from its nib.

self.delegate = self;

if (self.delegate
&& [self.delegate
respondsToSelector:@selector(selfDelegateMethod)])
{

[self.delegate selfDelegateMethod];

}

}

pragma

mark - ViewControllerDelegate method

實現(xiàn)協(xié)議中的方法

  • (void)selfDelegateMethod

{

NSLog(@"自己委托自己實現(xiàn)的方法");

}

看出這種寫法的奇葩地方了嗎?自己委托自己去實現(xiàn)某個方法,而不是委托別的類去實現(xiàn)某個方法。本類中定義的一個Block其實就是閑的蛋疼,委托自己去字做某件事情,實際的意義不大,所以你很少看見別人的代碼直接在類中定義Block然后使用的,Block很多的用處是跨越兩個類來使用的,比如作為property屬性或者作為方法的參數(shù),這樣就能跨越兩個類了。

2、第二部分

__block關(guān)鍵字的使用

在Block的{}體內(nèi),是不可以對外面的變量進行更改的,比如下面的語句,

  • (void)viewDidLoad

{

將Block定義在方法內(nèi)部

int x = 100;

void (^sumXAndYBlock)(int)
= ^(int y){

x = x+y;

printf("new x value is %d",x);

};

sumXAndYBlock(50);

}

這段代碼有什么問題呢,Xcode會提示x變量錯誤信息:Variable is not assigning (missing __blocktype),這時候給int
x = 100;語句前面加上__block關(guān)鍵字即可,如下:

__block int x = 100;

這樣在Block的{}體內(nèi),就可以修改外部變量了。

3、第三部分:Block作為property屬性實現(xiàn)頁面之間傳值

需求:在ViewController中,點擊Button,push到下一個頁面NextViewController,在NextViewController的輸入框TextField中輸入一串字符,返回的時候,在ViewController的Label上面顯示文字內(nèi)容,

(1)第一種方法:首先看看通過“協(xié)議/代理”是怎么實現(xiàn)兩個頁面之間傳值的吧,

NextViewController是push進入的第二個頁面

NextViewController.h 文件

定義一個協(xié)議,前一個頁面ViewController要服從該協(xié)議,并且實現(xiàn)協(xié)議中的方法

@protocol NextViewControllerDelegate
<NSObject>

  • (void)passTextValue:(NSString *)tfText;

@end

@interface NextViewController : UIViewController

@property (nonatomic,
assign) id<NextViewControllerDelegate>

delegate;

@end

NextViewController.m 文件

點擊Button返回前一個ViewController頁面

  • (IBAction)popBtnClicked:(id)sender
    {

    if (self.delegate
    && [self.delegate
    respondsToSelector:@selector(passTextValue:)])
    {

      self.inputTF是該頁面中的TextField輸入框
    

    [self.delegate passTextValue:self.inputTF.text];

    }

    [self.navigationController
    popViewControllerAnimated:YES];

}

接下來我們在看看ViewController文件中的內(nèi)容,

ViewController.m 文件

@interface ViewController
()<NextViewControllerDelegate>

@property (strong,
nonatomic) IBOutlet
UILabel *nextVCInfoLabel;

@end

點擊Button進入下一個NextViewController頁面

  • (IBAction)btnClicked:(id)sender

{

NextViewController *nextVC = [[NextViewController alloc] initWithNibName:@"NextViewController" bundle:nil];

nextVC.delegate = self;//設(shè)置代理

[self.navigationController

pushViewController:nextVC animated:YES];

}

實現(xiàn)協(xié)議NextViewControllerDelegate中的方法

pragma

mark - NextViewControllerDelegate method

  • (void)passTextValue:(NSString *)tfText

{

self.nextVCInfoLabel是顯示NextViewController傳遞過來的字符串Label對象

self.nextVCInfoLabel.text = tfText;

}

這是通過“協(xié)議/代理”來實現(xiàn)的兩個頁面之間傳值的方式。

(2)第二種方法:使用Block作為property,實現(xiàn)兩個頁面之間傳值,

先看看NextViewController文件中的內(nèi)容,

NextViewController.h 文件

@interface NextViewController : UIViewController

@property (nonatomic,
copy) void
(^NextViewControllerBlock)(NSString *tfText);

@end

NextViewContorller.m 文件

  • (IBAction)popBtnClicked:(id)sender
    {

    if (self.NextViewControllerBlock)
    {

self.NextViewControllerBlock(self.inputTF.text);

}

[self.navigationController

popViewControllerAnimated:YES];

}

再來看看ViewController文件中的內(nèi)容,

  • (IBAction)btnClicked:(id)sender

{

NextViewController *nextVC = [[NextViewController alloc] initWithNibName:@"NextViewController" bundle:nil];

nextVC.NextViewControllerBlock = ^(NSString *tfText){

[self resetLabel:tfText];

};

[self.navigationController

pushViewController:nextVC animated:YES];

}

pragma

mark - NextViewControllerBlock method

  • (void)resetLabel:(NSString *)textStr

{

self.nextVCInfoLabel.text = textStr;

}

好了就這么多代碼,可以使用Block來實現(xiàn)兩個頁面之間傳值的目的,實際上就是取代了Delegate的功能。

另外,博客中的代碼Sample Code可以再Github下載,如果因為Github被墻了,可以在終端使用git clone + 完整鏈接,即可克隆項目到本地。

Github中的代碼,可以開啟兩種調(diào)試模式,你需要在項目的配置文件BlockSamp-Prefix.pch中注釋或者解注釋下面的代碼:

define

Debug_BlcokPassValueEnable

即可開啟兩種調(diào)試的方式,如果注釋了上面的語句就是使用Delegate進行調(diào)試;否則使用Block進行調(diào)試。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

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