你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS之block淺談

iOS之block淺談

編輯:IOS開發綜合

前言

ios4.0系統已開始支持block,在編程過程中,block被Obj-C看成是對象,它封裝了一段代碼,這段代碼可以在任何時候執行。block可以作為函數參數或者函數的返回值,而其本身又可以帶輸入參數或返回值。它和傳統的函數指針很類似,但是有區別:block是inline的,並且它對局部變量是只讀的。

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

block的使用

1、block的定義

// 聲明和實現寫在一起,就像變量的聲明實現 int a = 10;

int(^aBlock)(int,int) = ^(intnum1,intnum2) {

returnnum1 * num2;

};

// 聲明和實現分開,就像變量先聲明後實現 int a;a = 10;

int(^cBlock)(int,int);

cBlock = ^(intnum1,intnum2)

{

returnnum1 * num2;

};

其中,定義了一個名字為aBlock的blocks對象,並攜帶了相關信息:

1、aBlock 有兩個形式參數,分別為int類型;

2、aBlock 的返回值為int 類型;

3、等式右邊就是blocks的具體實現;

4、^ 帶邊blocks聲明和實現的標示(關鍵字);

當然,你可以定義其他形式的block。e.g:無返回值,無形式參數等;

void(^bBlock)() = ^()

{

inta =10;

printf(num = %d,a);

};

2、blocks 訪問權限

blocks可以訪問局部變量,但是不能修改。

inta =10;

int(^dBlock)(int) = ^(intnum)

{

a++;//not work!

returnnum * a;

};

此處不能修改的原因是在編譯期間確定的,編譯器編譯的時候把a的值復制到block作為一個新變量(假設是a‘ = 10),此時a'和a是沒有關系的。

這個地方就是函數中的值傳遞。如果要修改就要加關鍵字:__block或者static

__blockinta =7;

int(^dBlock)(int) = ^(intnum)

{

a++;// work!

returnnum * a;

};

3、block的調用

block調用就像調用函數一樣。e.g:

1intc = aBlock(10,10);

bBlock();

4、block 應用

假設我們熟悉代理遞值的話,對代理我們可能又愛有恨!我們先建立模型A頁面 push

B頁面,如果把A頁面的值傳遞到B頁面,屬性和單例傳值可以搞定!但是如果Pop過程中把B頁面的值傳遞到A頁面,那就可以用單例或者代理了!說到代理,

我們要先聲明協議,創建代理,很是麻煩。常常我們傳遞一個數值需要在兩個頁面間寫很多代碼,這些代碼改變頁面的整體順序,可讀性也打了折扣。所以,此

時,block是一種優化方案!

5、 block的內存管理

block本身是像對象一樣可以retain,和release。但是,block在創建的時候,它的內存是分配在棧(stack)上,而不是在堆(heap)上。他本身的作於域是屬於創建時候的作用域,一旦在創建時候的作用域外面調用block將導致程序崩潰。比如下面的例子。 我在view did load中創建了一個block:

- (void)viewDidLoad

{

[superviewDidLoad];

int number = 1;

_block = ^(){

NSLog(@number %d, number);

};

}

並且在一個按鈕的事件中調用了這個block:

- (IBAction)testDidClick:(id)sender {

_block();

}

此時我按了按鈕之後就會導致程序崩潰,解決這個問題的方法就是在創建完block的時候需要調用copy的方法。copy會把block從棧上移動到堆上,那麼就可以在其他地方使用這個block了~ 修改代碼如下:

_block = ^(){

NSLog(@number %d, number);

};

_block = [_blockcopy];

同理,特別需要注意的地方就是在把block放到集合類當中去的時候,如果直接把生成的block放入到集合類中,是無法在其他地方使用block,必須要對block進行copy。不過代碼看上去相對奇怪一些:

[array addObject:[[^{

NSLog(@hello!);

} copy] autorelease]];

6、循環引用

對於非ARC下, 為了防止循環引用, 我們使用__block來修飾在Block中使用的對象:

對於ARC下, 為了防止循環引用, 我們使用__weak來修飾在Block中使用的對象。原理就是:ARC中,Block中如果引用了__strong修飾符的自動變量,則相當於Block對該變量的引用計數+1。

這一點其實是在第一點的一個小的衍生。當在block內部使用成員變量的時候,比如


@interface ViewController : UIViewController

{

NSString *_string;

}

@end

在block創建中:

_block = ^(){

NSLog(@string %@, _string);

};

這裡的_string相當於是self->_string;那麼block是會對內部的對象進行一次retain。也就是說,self會被retain一次。當self釋放的時候,需要block釋放後才會對self進行釋放,但是block的釋放又需要等self的dealloc中才會釋放。如此一來變形成了循環引用,導致內存洩露。

修改方案是新建一個__block scope的局部變量,並把self賦值給它,而在block內部則使用這個局部變量來進行取值。因為__block標記的變量是不會被自動retain的。

__block ViewController *controller = self;

_block = ^(){

NSLog(@string %@, controller->_string);

};

博主浏覽了很多博客,總結了一下,block實際上是(底層c++): 指向結構體的指針,底層會創建一個結構體,實現構造方法,來接參數,編譯器會將block的內部代碼生成對應的函數。

block實戰:用block進行頁面間傳值

使用Block的地方很多,其中傳值只是其中的一小部分,下面介紹Block在兩個界面之間的傳值:

先說一下思想:

首先,創建兩個視圖控制器,在第一個視圖控制器中創建一個UILabel和一個UIButton,其中UILabel是為了顯示第二個視圖控制器傳過來的字符串,UIButton是為了push到第二個界面。

第二個界面的只有一個UITextField,是為了輸入文字,當輸入文字,並且返回第一個界面的時候,當第二個視圖將要消失的時候,就將第二個界面上TextFiled中的文字傳給第一個界面,並且顯示在UILabel上。

其實核心代碼就幾行代碼:

下面是主要代碼:(因為我是用storyBoard創建的工程,所以上面的屬性和相應的方法,是使用系統生成的outlet)

一、在第二個視圖控制器的.h文件中定義聲明Block屬性

typedef void(^ReturnTextBlock)(NSString *showText);

@interface TextFieldViewController : UIViewController

@property(nonatomic, copy) ReturnTextBlock returnTextBlock;

- (void)returnText:(ReturnTextBlock)block;

@end

第一行代碼是為要聲明的Block重新定義了一個名字

ReturnTextBlock

這樣,下面在使用的時候就會很方便。

第三行是定義的一個Block屬性

第四行是一個在第一個界面傳進來一個Block語句塊的函數,不用也可以,不過加上會減少代碼的書寫量

二、實現第二個視圖控制器的方法

- (void)returnText:(ReturnTextBlock)block {

self.returnTextBlock = block;

}

- (void)viewWillDisappear:(BOOL)animated {

if(self.returnTextBlock != nil) {

self.returnTextBlock(self.inputTF.text);

}

}

其中inputTF是視圖中的UITextField。

第一個方法就是定義的那個方法,把傳進來的Block語句塊保存到本類的實例變量returnTextBlock(.h中定義的屬性)中,然後尋找一個時機調用,而這個時機就是上面說到的,當視圖將要消失的時候,需要重寫:

- (void)viewWillDisappear:(BOOL)animated;

方法。

三、在第一個視圖中獲得第二個視圖控制器,並且用第二個視圖控制器來調用定義的屬性

如下方法中書寫:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

{

// Get the new view controller using [segue destinationViewController].

// Pass the selected object to the new view controller.

TextFieldViewController *tfVC = segue.destinationViewController;

[tfVC returnText:^(NSString *showText) {

self.showLabel.text = showText;

}];

}

揭開block神秘面紗

假設A有一個任務,是去倉庫取一張A4紙放到會議室,然後在紙上寫一份策劃書。取紙又要經過倉庫管理員,於是A通知倉庫管理員來張紙過來。由於管理員是個老

頭動作很慢,另外A還有其他工作,如果一直等待管理員就太浪費時間了,合理的做法是讓倉庫管理員進行取紙這項工作,A在通知管理員後就繼續自己的工作。A

不知道倉庫管理員什麼時候能完成取紙,也就不知道什麼時候可以在紙上寫策劃書。這時block機制就派上用場了,使用這種機制,可以讓A在通知倉庫管理員

取紙的同時,告訴倉庫管理員將紙放到編號XX會議室,並安排好將要在紙上寫的策劃內容,當管理員把紙拿來後,可能過一會就會有個助理將策劃內容寫到紙上。

我們將這個故事對應到代碼上:

#pragmamark-第三方登錄

-(void)btnLoginWeiboClicked:(id)sender{

[_waitCirclestartAnimating];

[[HSLoginClasscreateInstance]loginWithPlatformType:ShareTypeSinaWeibowithBlock:^(BOOLsuccess,idmessage){

if(success){

//跳出登錄頁面

[selfdismissViewControllerAnimated:YEScompletion:^{}];

[_waitCirclestopAnimating];

NSLog(@"新浪微博%@",message);

}else{

}

}];

//teststatistics

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{

NSDate*date=[NSDatedate];

NSTimeIntervalnowTime=[datetimeIntervalSince1970];

NSString*netStatus=(NSString*)[[NSUserDefaultsstandardUserDefaults]objectForKey:NETSTATUS];

[[HSStatisticsModulestatisticsModule]makeSession:nowTime:OPER_IN:@"noid"];

[[HSStatisticsModulestatisticsModule]upLoadData:netStatus:[NSStringstringWithFormat:@"%f",nowTime]];

});

}

請看這段代碼,整個方法是A要做的工作,startAnimating/loginWithPlatformType/dispatch_async分別是A要做的三個任務,由於loginWithPlatformType需要一段時間才能完成,並且loginWithPlatformType完成後要根據結果做相應處理,所以我們對loginWithPlatformType進行異步處理。block代碼段是loginWithPlatformType得到結果後要做的操作H,這裡的block寫法就表示,我們先將操作H安排好,然後繼續其他工作,當loginWithPlatformType執行ok後自會有人(可能是那個助理)去執行操作H。

iOS面試之block

1 什麼是block

對於閉包(block),

有很多定義,其中閉包就是能夠讀取其它函數內部變量的函數,這個定義即接近本質又較好理解。對於剛接觸Block的同學,會覺得有些繞,因為我們習慣寫這

樣的程序main(){ funA();} funA(){funB();} funB(){.....};

就是函數main調用函數A,函數A調用函數B...

函數們依次順序執行,但現實中不全是這樣的,例如項目經理M,手下有3個程序員A、B、C,當他給程序員A安排實現功能F1時,他並不等著A完成之後,再

去安排B去實現F2,而是安排給A功能F1,B功能F2,C功能F3,然後可能去寫技術文檔,而當A遇到問題時,他會來找項目經理M,當B做完時,會通知

M,這就是一個異步執行的例子。在這種情形下,Block便可大顯身手,因為在項目經理M,給A安排工作時,同時會告訴A若果遇到困難,如何能找到他報告

問題(例如打他手機號),這就是項目經理M給A的一個回調接口,要回掉的操作,比如接到電話,百度查詢後,返回網頁內容給A,這就是一個Block,在M

交待工作時,已經定義好,並且取得了F1的任務號(局部變量),卻是在當A遇到問題時,才調用執行,跨函數在項目經理M查詢百度,獲得結果後回調該

block。

2 block 實現原理

Objective-C是對C語言的擴展,block的實現是基於指針和函數指針。

從計算語言的發展,最早的goto,高級語言的指針,到面向對象語言的block,從機器的思維,一步步接近人的思維,以方便開發人員更為高效、直接的描述出現實的邏輯(需求)。

 

3 block的使用

 

使用typed聲明block

typedef void(^didFinishBlock) (NSObject *ob);

這就聲明了一個didFinishBlock類型的block,

然後便可用

@property (nonatomic,copy) didFinishBlock finishBlock;

聲明一個block對象,注意對象屬性設置為copy,接到block 參數時,便會自動復制一份。

__block是一種特殊類型,

使用該關鍵字聲明的局部變量,可以被block所改變,並且其在原函數中的值會被改變。

4 常見系列面試題

面試時,面試官會先問一些,是否了解block,是否使用過block,這些問題相當於開場白,往往是下面一系列問題的開始,所以一定要如實根據自己的情況回答。

1 使用block和使用delegate完成委托模式有什麼優點?

首先要了解什麼是委托模式,委托模式在iOS中大量應用,其在設計模式中是適配器模式中的對象適配器,Objective-C中使用id類型指向一切對象,使委托模式在iOS中的實現更為方便。了解委托模式的細節:

使用block實現委托模式,其優點是回調的block代碼塊定義在委托對象函數內部,使代碼更為緊湊;

適配對象不再需要實現具體某個protocol,代碼更為簡潔。

2 多線程與block

GCD與Block

使用 dispatch_async系列方法,可以以指定的方式執行block

 

dispatch_async的完整定義

void dispatch_async(

dispatch_queue_t queue,

dispatch_block_t block);

功能:在指定的隊列裡提交一個異步執行的block,不阻塞當前線程

通過queue來控制block執行的線程。主線程執行前文定義的 finishBlock對象

dispatch_async(dispatch_get_main_queue(),^(void){finishBlock();});

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved