久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

[ios開發(fā)基礎(chǔ)之]代碼塊

 方海龍的書館 2014-12-18

    iOS4引入了一個(gè)新特性,支持代碼塊的使用,, 這將從根本上改變你的編程方式,。代碼塊是對(duì)C語言的一個(gè)擴(kuò)展,因此在Objective-C中完全支持,。如果你學(xué)過Ruby,Python或Lisp編程 語言,那么你肯定知道代碼塊的強(qiáng)大之處,。簡單的說,,你可以通過代碼塊封裝一組代碼語句并將其當(dāng)作一個(gè)對(duì)象。代碼塊的使用是一種新的編碼風(fēng)格,,可以讓你運(yùn)用 自如的使用iOS4中新增API,。
    我們先來看兩個(gè)在iOS4中使用代碼塊的例子(你很有可能已經(jīng)見過):view animations 和enumeration
使用代碼塊的例子
     第一個(gè)例子,假設(shè)我們創(chuàng)建一個(gè)紙牌游戲,,需要展現(xiàn)紙牌被派發(fā)到玩家面前的動(dòng)畫效果,。幸運(yùn)的是通過UIKit框架可以很容易的實(shí)現(xiàn)一個(gè)動(dòng)畫效果。但是最終是什么樣的動(dòng)畫是由你的程序決定的,。你可以在代碼塊中指定動(dòng)畫的內(nèi)容然后再將代碼塊傳給animateWithDuration:animations:方法,,像下面這樣:
[UIView animateWithDuration:2.0
    animations:^ {
        self.cardView.alpha = 1.0;
        self.cardView.frame = CGRectMake(176.0, 258.0, 72.0, 96.0);
        self.cardView.transform = CGAffineTransformMakeRotation(M_PI);
    }
];

    當(dāng)這個(gè)動(dòng)畫代碼塊執(zhí)行時(shí),我們的紙牌會(huì)展現(xiàn)三種方式的動(dòng)畫:改變它的alpha值從而淡入顯示,,改變它的位置到右下角(玩家的位置),,以及自轉(zhuǎn)180度(為了使其效果更好)。
第二個(gè)代碼塊的例子是迭代一個(gè)紙牌的集合,,并打印其名字和在集合里的索引值,。
你可以通過使用for循環(huán)來達(dá)到目的,但是在iOS4中NSArray類有一個(gè)使用了代碼塊的方便方法:enumerateObjectsUsingBlock:,。下面是如何使用它:

NSArray *cards = [NSArray arrayWithObjects:@"Jack", @"Queen", @"King", @"Ace", nil];
 
[cards enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
    NSLog(@"%@ card at index %d", object, index);
}];

    這個(gè)代碼塊使用了三個(gè)參數(shù):數(shù)組中的一個(gè)對(duì)象,,該對(duì)象的索引,以及一個(gè)標(biāo)識(shí)迭代是否結(jié)束的標(biāo)志,。我們稍候再對(duì)其進(jìn)一步探討,。enumerateObjectsUsingBlock: 這個(gè)方法會(huì)將集合中的每一個(gè)元素傳入相應(yīng)的參數(shù)并調(diào)用代碼塊中的方法,。
    因此在你的mac和iOS程序中使用代碼塊的優(yōu)勢(shì)是:它允許你附加任意的代碼到蘋果官方提供的方法上。盡管在概念上與代理相似,,但是在方法中使用簡短的內(nèi)聯(lián)代碼塊往往更加方便,,更加優(yōu)雅。
    這是一個(gè)好的開始,,但重要的是要明白它內(nèi)部的處理,。當(dāng)我學(xué)習(xí)新東西的時(shí)候,我喜歡先將其分為一個(gè)個(gè)簡單的部分,,了解它們?nèi)绾喂ぷ?,然后再將它們組裝到一塊,這樣我會(huì)對(duì)自己寫的代碼以及快速解決出現(xiàn)的問題充滿信心,。因此,,讓我們先回頭學(xué)習(xí)下如何聲明和調(diào)用簡單的代碼塊。
代碼塊的基本概念
    一個(gè)代碼塊可以簡單看作是一組可執(zhí)行的代碼,。例如,,下面是一個(gè)打印當(dāng)前日期和時(shí)間的代碼塊:
^ {
    NSDate *date = [NSDate date];
    NSLog(@"The date and time is %@", date);
};
    插 入符號(hào)(^)聲明一個(gè)代碼塊的開始,一對(duì)大括號(hào){}構(gòu)成了代碼塊的體部,。你可以認(rèn)為代碼塊與一個(gè)匿名函數(shù)類似,。那么,如果是一個(gè)匿名的函數(shù),,我們?cè)撛趺凑{(diào) 用這個(gè)代碼塊呢,?最常見使用代碼塊的方式是將其傳入方法中供方法回調(diào),就像之前我們已經(jīng)見到了view animations 和enumeration,。另一種使用代碼塊的方式是將其賦予代碼塊變量,,然后可使用該變量來直接調(diào)用代碼塊。以下是如何聲明我們的代碼塊并將它賦予代碼 塊變量now:
void (^now)(void) = ^ {
    NSDate *date = [NSDate date];
    NSLog(@"The date and time is %@", date);
};
    聲明一個(gè)塊變量的語法需要一些時(shí)間適應(yīng),,這才有趣,。如果你使用過函數(shù)指針,代碼塊變量與其類似,。在上面代碼等號(hào)右邊是我們已經(jīng)介紹過的代碼塊,。等號(hào)左邊我們聲明了一個(gè)代碼塊變量now。

 
    代碼塊變量之前有^符號(hào)并被小括號(hào)包著,,代碼塊變量有類型定義的,。因此,上圖中的now變量可以應(yīng)用任何無參,,無返回值的代碼塊,。我們之前聲明的代碼塊符合這要求,,所以我們可以放心的把它分配給now變量,。
    只要有一個(gè)代碼塊變量,,并在其作用域范圍內(nèi),我們就可以像調(diào)用函數(shù)一樣來調(diào)用它,。下面是如何調(diào)用我們的代碼塊:
now();
    你可以在C函數(shù)或者Objective-c方法中聲明代碼塊變量,,然后在同一作用域內(nèi)調(diào)用它,就像我們前面說明那樣,。當(dāng)代碼塊執(zhí)行時(shí),,它打印當(dāng)前的日期和時(shí)間。目前為止,,進(jìn)展順利,。
代碼塊是閉包
    如 果這就是代碼塊的全部的話,那么他與函數(shù)是完全相同的,。但事實(shí)是代碼塊不僅僅是一組可執(zhí)行的代碼,。代碼塊能夠捕捉到已聲明的同一作用域內(nèi)的變量,同時(shí)由于 代碼塊是閉包,,在代碼塊聲明時(shí)就將使用的變量包含到了代碼塊范圍內(nèi),。為了說明這一點(diǎn),讓我們改變一下前面的例子,,將日期的初始化移到代碼塊之外,。
NSDate *date = [NSDate date];
 
void (^now)(void) = ^ {
    NSLog(@"The date and time is %@", date);
};
 
now();
    當(dāng)你第一次調(diào)用這個(gè)代碼塊的時(shí)候,它與我們之前的版本結(jié)果完全一致:打印當(dāng)前的日期和時(shí)間,。但是當(dāng)我們改變?nèi)掌诤笤僬{(diào)用代碼塊,那么就會(huì)有顯著的不同了,,
sleep(5);
 
date = [NSDate date];
  
now();
    盡 管我們?cè)谡{(diào)用代碼塊之前改變了日期,,但是當(dāng)代碼塊調(diào)用時(shí)仍然打印的是之前的日期和時(shí)間。就像是日期在代碼塊聲明時(shí)停頓了一樣,。為什么會(huì)這樣呢,,當(dāng)程序執(zhí)行 到代碼塊的聲明時(shí),代碼塊對(duì)同一作用域并且塊內(nèi)用到的變量做一個(gè)只讀的備份,。你可以認(rèn)為變量在代碼塊內(nèi)被凍結(jié)了,。因此,不論何時(shí)當(dāng)代碼塊被調(diào)用時(shí),,立即調(diào) 用或5秒鐘之后,,只要在程序退出之前,它都是打印最初的日期和時(shí)間,。
    事實(shí)上,,上面那個(gè)展示代碼塊是閉包的例子并不十分完善,畢竟,你可以將日期作為一個(gè)參數(shù)傳入到代碼塊中(下面講解),。但是當(dāng)你將代碼塊在不同方法間傳遞時(shí)閉包的特性就會(huì)變得十分有用,,因?yàn)樗锩娴淖兞渴潜3植蛔兊摹?br style="margin-top:0px; margin-right:0px; margin-bottom:0px; margin-left:0px; padding-top:0px; padding-right:0px; padding-bottom:0px; padding-left:0px"> 代碼塊參數(shù)
    就像函數(shù)一樣,代碼塊可以傳入?yún)?shù)和返回結(jié)果,。例如,,我們想要一個(gè)能夠返回指定數(shù)的三倍的代碼塊,下面是實(shí)現(xiàn)的代碼塊:
^(int number) {
    return number * 3;
};
    為代碼塊聲明一個(gè)變量triple,,如下:
int (^triple)(int) = ^(int number) {
    return number * 3;
};
    上面說過,,我們需要熟悉等號(hào)左邊聲明代碼塊變量的語法。現(xiàn)在讓我們從左到右分開來說明:

 
最 左邊的int是返回值類型,,中間是小括號(hào)包圍插入符號(hào)^及代碼塊變量的名字,,最后又一個(gè)小括號(hào),包圍著參數(shù)的類型(上面例子中只有一個(gè)int參數(shù)),。等號(hào) 右邊的代碼塊聲明必須符合左側(cè)的定義,。有一點(diǎn)要說明的是,為了方便,,可以不聲明代碼塊的返回類型,,編譯器會(huì)從返回語句中做出判斷。
    要調(diào)用這個(gè)代碼塊,,你需要傳入一個(gè)需要乘3的參數(shù),,并接受返回值,像這樣:
int result = triple(2);

    下面你將知道如何聲明并創(chuàng)建一個(gè)需要兩個(gè)int型參數(shù),,將它們相乘然后返回結(jié)果的代碼塊:
int (^multiply)(int, int) = ^(int x, int y) {
    return x * y;
};

    這是如何調(diào)用這個(gè)代碼塊:
int result = multiply(2, 3);

    聲明代碼塊變量使我們有機(jī)會(huì)探討代碼塊類型以及如何調(diào)用,。代碼塊變量類似函數(shù)指針,調(diào)用代碼塊與調(diào)用函數(shù)相似,。不同于函數(shù)指針的是,,代碼塊實(shí)際上是Objective-C對(duì)象,這意味著我們可以像對(duì)象一樣傳遞它們,。
調(diào)用代碼塊的方法
    在實(shí)際中,,代碼塊經(jīng)常被作為參數(shù)傳入方法中供其回調(diào)。當(dāng)把代碼塊作為一個(gè)參數(shù)時(shí),,相比分配一個(gè)代碼塊變量,,更通常的做法是作為內(nèi)聯(lián)代碼塊。例如,,我們之前看到的例子:view animations 和enumeration,。
    蘋 果官方已經(jīng)增加了一些使用代碼塊的方法到他們的框架中。你也可以寫一些使用代碼塊的API了,。例如,,我們要?jiǎng)?chuàng)建一個(gè)Worker類的使用代碼塊的類方法,, 該方法重復(fù)調(diào)用代碼塊指定的次數(shù),并處理代碼塊每次返回的結(jié)果,。下面是我們使用內(nèi)聯(lián)代碼塊調(diào)用這個(gè)方法,,代碼塊負(fù)責(zé)返回1到10的每個(gè)數(shù)的三倍,。
[Worker repeat:10 withBlock:^(int number) {
    return number * 3;
}];

    這個(gè)方法可以將任何接受一個(gè)int型參數(shù)并返回一個(gè)int型結(jié)果的代碼塊作為參數(shù),,如果想得到數(shù)字的二倍,,只需要改變傳入方法的代碼塊,。

編寫使用代碼塊的方法
    在第一部分我們留下了一個(gè)任務(wù):寫一個(gè)Work類的調(diào)用代碼塊的類方法,并且重復(fù)調(diào)用代碼塊指定的次數(shù),,還要處理每次代碼塊的返回值,。如果我們想要得到1到5的三倍的話,,那么下面是我們?cè)撊绾握{(diào)這個(gè)帶有內(nèi)聯(lián)代碼塊的方法:
[Worker repeat:5 withBlock:^(int number) {
    return number * 3;
}];
    我 經(jīng)常這樣設(shè)計(jì)一個(gè)類,首先寫代碼調(diào)用一個(gè)虛構(gòu)的方法,,這也是在提交之前一種形成API的簡單方式,,一旦認(rèn)為這個(gè)方法調(diào)用正確,我就去實(shí)現(xiàn)這個(gè)方法。這樣,, 那個(gè)方法的名字是repeat:withBlock:,,我認(rèn)為不合適(我知道在第一部分是叫這個(gè)名字,但我已經(jīng)改變注意了),。這個(gè)名字容易使人混淆,,因?yàn)?該方法實(shí)際上并不是重復(fù)做相同的事情。這個(gè)方法從1迭代到指定的次數(shù),,并處理代碼塊的返回,。所以讓我們開始正確的重命名它:
[Worker iterateFromOneTo:5 withBlock:^(int number) {
    return number * 3;
}];
    我對(duì)這個(gè)使用兩個(gè)參數(shù)的方法的名字iterateFromOneTo:withBlock:很滿意,一個(gè)int型參數(shù)表示調(diào)用代碼塊的次數(shù)和一個(gè)要被調(diào)用的代碼塊參數(shù)?,F(xiàn)在讓我們?nèi)?shí)現(xiàn)這個(gè)方法,。
    對(duì) 于初學(xué)者,我么該如何聲明這個(gè) iterateFromOneTo:withBlock:方法呢,?首先我們需要知道所有參數(shù)的類型,,第一個(gè)參數(shù)很容易,是個(gè)int類型,;第二個(gè)參數(shù)是一個(gè) 代碼塊,代碼塊是有返回類型的,。在這個(gè)例子中,,這個(gè)方法可以接受任何有一個(gè)int型參數(shù)并返回int型結(jié)果的代碼塊作為參數(shù)。下面是實(shí)際的代碼塊類型:
int (^)(int)
    已經(jīng)有了方法的名字和它的參數(shù)類型,,我們就可以聲明這個(gè)方法了,。這是Worker類的類方法,我們?cè)趙orker.h中聲明它:
@interface Worker : NSObject {
}
 
+ (void)iterateFromOneTo:(int)limit withBlock:(int (^)(int))block;
 
@end
    第 一眼看去,,代碼塊參數(shù)不容易理解,。有個(gè)要記住訣竅是:在Objective-C中所有的方法參數(shù)有兩個(gè)部分組成。被括起來的參數(shù)類型以及參數(shù)的名稱。這個(gè) 例子中,,參數(shù)的要求是一個(gè)是int型和一個(gè)是int(^)(int)型的代碼塊(你可以為參數(shù)命名為任意的名字,,不一定非得是block)。這個(gè)方法的實(shí) 現(xiàn)是在Worker.m文件文件中,,比較簡單:
#import "Worker.h"
 
@implementation Worker
 
+ (void)iterateFromOneTo:(int)limit withBlock:(int (^)(int))block {
    for (int i = 1; i <= limit; i++) {
        int result = block(i);
        NSLog(@"iteration %d => %d", i, result);
    }
}
 
@end
    方 法通過一個(gè)循環(huán)來每次調(diào)用代碼塊,,并打印出代碼塊的返回結(jié)果。記住一旦我們?cè)谧饔糜騼?nèi)有一個(gè)代碼塊變量,,那么就可以像函數(shù)一樣使用它,。在這里代碼塊參數(shù)就 是一個(gè)代碼塊變量。因此,,當(dāng)執(zhí)行block(i)時(shí)就會(huì)調(diào)用傳入的代碼塊,。當(dāng)代碼塊返回結(jié)果后會(huì)繼續(xù)往下執(zhí)行。現(xiàn)在我們可以使用內(nèi)聯(lián)代碼塊的方式調(diào)用 iterateFromOneTo:withBlock:方法,,像這樣:
[Worker iterateFromOneTo:5 withBlock:^(int number) {
    return number * 3;
}];
我們也可以不使用內(nèi)聯(lián)代碼塊的方式,,傳入一個(gè)代碼塊變量作為參數(shù):
int (^tripler)(int) = ^(int number) {
    return number * 3;
};
 
[Worker iterateFromOneTo:5 withBlock:tripler];
不論那種方式,我們得到的輸出如下:
iteration 1 => 3
iteration 2 => 6
iteration 3 => 9
iteration 4 => 12
iteration 5 => 15
    當(dāng)然我們可以傳入進(jìn)行任何運(yùn)算的代碼塊,。想要得到數(shù)字的平方嗎,?沒問題,只要傳入一個(gè)不同的代碼塊:
[Worker iterateFromOneTo:5 withBlock:^(int number) {
    return number * number;
}];
現(xiàn)在我們的代碼是可以運(yùn)行的,,下面將代碼稍微整理下吧,。
善于使用Typedef
    匆忙的聲明代碼塊的類型容易混亂,即使在這個(gè)簡單的例子中,,函數(shù)指正的語法還是有許多不足之處:
+ (void)iterateFromOneTo:(int)limit withBlock:(int (^)(int))block;
    試想代碼塊要使用多個(gè)參數(shù),,并且有些參數(shù)是指針類型,這樣的話你幾乎需要完全重寫你的代碼,。為了提高可讀性和避免在.h和.m中出項(xiàng)重復(fù),,我們可以使用typedef修改Worker.h文件:
typedef int (^ComputationBlock)(int);
 
@interface Worker : NSObject {
}
 
+ (void)iterateFromOneTo:(int)limit withBlock:(ComputationBlock)block;
 
@end
    typedef 是C語言的一個(gè)關(guān)鍵字,其作用可以理解為將一個(gè)繁瑣的名字起了一個(gè)昵稱,。在這種情況下,,我們定義一個(gè)代碼塊變量ComputationBlock,它有一 個(gè)int型參數(shù)和一個(gè)int型返回值,。然后,,我們定義iterateFromOneTo:withBlock:方法時(shí),可以直接使用 ComputationBlock作為代碼塊參數(shù),。同樣,,在Worker.m文件,我們可以通過使用ComputationBlock簡化代碼:
#import "Worker.h"
 
@implementation Worker
 
+ (void)iterateFromOneTo:(int)limit withBlock:(ComputationBlock)block {
    for (int i = 1; i <= limit; i++) {
        int result = block(i);
        NSLog(@"iteration %d => %d", i, result);
    }
}
 
@end
    嗯,, 這樣就好多了,,代碼易于閱讀,,沒有在多個(gè)文件重復(fù)定義代碼塊類型。事實(shí)上,,你可以使用ComputationBlock在你程序的任何地方,,只要 import “Worker.h”,你會(huì)碰到類似的typedef在新的iOS4的API中,。例如,,ALAssetsLibrary類定義了下面的方法:
- (void)assetForURL:(NSURL *)assetURL      
        resultBlock:(ALAssetsLibraryAssetForURLResultBlock)resultBlock 
       failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock
    這個(gè)方法調(diào)用兩個(gè)代碼塊,一個(gè)代碼塊時(shí)找到所需的資源時(shí)調(diào)用,,另一個(gè)時(shí)沒找到時(shí)調(diào)用,。它們 的 typedef如下:
typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset);
typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);
    然后在你的程序中可以使用ALAssetsLibraryAssetForURLResultBlock和ALAssetsLibraryAccessFailureBlock去表示相應(yīng)的代碼塊變量。
    我建議在寫一個(gè)使用代碼塊的公用方法時(shí)就用typedef,,這樣有助于你的代碼整潔,,并可以讓其他開發(fā)人員方便使用。
再來看一下閉包
    你應(yīng)該還記得代碼塊是閉包,,我們簡要的講述一下在第一部分提及的閉包,。在第一部分閉包的例子并不實(shí)用,而且我說閉包在方法間傳遞時(shí)會(huì)變得特別有用?,F(xiàn)在我們已經(jīng)知道如何寫一個(gè)實(shí)用代碼塊的方法,,那么就讓我們分析下另一個(gè)閉包的例子:
int multiplier = 3;
 
[Worker iterateFromOneTo:5 withBlock:^(int number) {
    return number * multiplier;
}];
    我們使用之前寫的iterateFromOneTo:withBlock:方法,有一點(diǎn)不同的是沒有將要得到的倍數(shù)硬編碼到代碼塊中,,這個(gè)倍數(shù)被聲明在代碼塊之外,,為一個(gè)本地變量。該方法執(zhí)行的結(jié)果與之前一致,,將1到5之間的數(shù)乘3:
iteration 1 => 3
iteration 2 => 6
iteration 3 => 9
iteration 4 => 12
iteration 5 => 15
    這個(gè)代碼的運(yùn)行是一個(gè)說明閉包強(qiáng)大的例子,。代碼打破了一般的作用域規(guī)則。實(shí)際上,,在iteratefromOneTo:withBlock:方法中調(diào)用multiplier變量,,可以把它看作是本地變量。
    記 住,,代碼塊會(huì)捕捉周圍的狀態(tài),。當(dāng)一個(gè)代碼塊聲明時(shí)它會(huì)自動(dòng)的對(duì)其內(nèi)部用到的變量做一個(gè)只讀的快照。因?yàn)槲覀兊拇a塊使用了multiplier變量,,這個(gè) 變量的值被代碼塊保存了一份供之后使用,。也就是說,multiplier變量已經(jīng)成為了代碼塊狀態(tài)啊的一部分,。當(dāng)代碼塊被傳入到 iterateFromOneTo:withBlock:方法,快的狀態(tài)也傳了進(jìn)去,。
    好吧,,如果我們想在代碼塊的內(nèi)部改變multiplier變量該怎么辦,?例如,代碼塊每次被調(diào)用時(shí)要讓multiplier變?yōu)樯弦淮斡?jì)算的結(jié)果,。你可能會(huì)試著在代碼塊里直接改變multiplier變量,,像這樣:
int multiplier = 3;
 
[Worker iterateFromOneTo:5 withBlock:^(int number) {
    multiplier = number * multiplier;
    return multiplier;  // compile error!
}];
    這樣的話是通不過編譯的,編譯器會(huì)報(bào)錯(cuò)“Assignment of read-only variable 'mutilplier'”,。這是因?yàn)榇a塊內(nèi)使用的是變量的副本,,它是堆棧里的一個(gè)常量。這些變量在代碼塊中是不可改變的,。
    如果你想要修改一個(gè)在塊外面定義,,在塊內(nèi)使用的變量時(shí),你需要在變量聲明時(shí)增加新的前綴_block,,像這樣:
__block int multiplier = 3;
 
[Worker iterateFromOneTo:5 withBlock:^(int number) {
    multiplier = number * multiplier;
    return multiplier;
}];
 
NSLog(@"multiplier  => %d", multiplier);
這樣代碼可以通過編譯,,運(yùn)行結(jié)果如下:
iteration 1 => 3
iteration 2 => 6
iteration 3 => 18
iteration 4 => 72
iteration 5 => 360
multiplier  => 360
    要 注意的是代碼塊運(yùn)行之后,multiplier變量的值已經(jīng)變?yōu)榱?60,。換句話說,,代碼塊內(nèi)部修改的不是變量的副本。聲明一個(gè)被_block修飾的變量 是將其引用傳入到了代碼塊內(nèi),。事實(shí)上,,被_block修飾的變量是被所有使用它的代碼塊共享的。這里要強(qiáng)調(diào)的一點(diǎn)是:_block不要隨便使用,。在將一些 東西移入內(nèi)存堆中會(huì)存在邊際成本,,除非你真的確定需要修改變量,否則不要用_block修飾符,。
編寫返回代碼塊的方法
    有時(shí)我們會(huì)需要編寫一個(gè)返回代碼塊的方法,。讓我先看一個(gè)錯(cuò)誤的例子:
+ (ComputationBlock)raisedToPower:(int)y {
    ComputationBlock block = ^(int x) {
        return (int)pow(x, y);
    };
    return block;  // Don't do this!
}
    這種方法簡單的創(chuàng)建了一個(gè)計(jì)算y的x次冪的代碼塊然后返回它。它使用了我們之前通過typedef使用的ComputationBlock,。下面是我們對(duì)所返回代碼塊的期望效果:
ComputationBlock block = [Worker raisedToPower:2];
block(3);  // 9
block(4);  // 16
block(5);  // 25
    在上面的例子中,,我們使用的得到代碼塊,傳入相應(yīng)的參數(shù),,它應(yīng)該會(huì)返回傳入值的平方,。但是當(dāng)我們運(yùn)行它時(shí),會(huì)得到運(yùn)行時(shí)錯(cuò)誤”EXC_BAD_ACCESS”,。
    怎么辦,?解決這個(gè)問題的關(guān)鍵是了解代碼塊是怎么分配內(nèi)存的。代碼塊的生命周期是在棧中開始的,,因?yàn)樵跅V蟹峙鋬?nèi)存是比較塊的,。是棧變量也就意味著它從棧中彈出后就會(huì)被銷毀。方法返回結(jié)果就會(huì)發(fā)生這樣的情況,。
    回 顧我們的raisedToPower:方法,,可以看到在方法中創(chuàng)建了代碼塊并將它返回,。這樣創(chuàng)建代碼塊就是已明確代碼塊的生存周期了,當(dāng)我們返回代碼塊變 量后,,代碼塊其實(shí)在內(nèi)存中已經(jīng)被銷毀了,。解決辦法是在返回之前將代碼塊從棧中移到堆中。這聽起來很復(fù)雜,,但是實(shí)際很簡單,,只需要簡單的對(duì)代碼塊進(jìn)行 copy操作,代碼塊就會(huì)移到堆中,。下面是修改后的方法,,它可以滿足我們的預(yù)期:
+ (ComputationBlock)raisedToPower:(int)y {
    ComputationBlock block = ^(int x) {
        return (int)pow(x, y);
    };
    return [[block copy] autorelease];
}
    注 意我們使用了copy后就必須跟一個(gè)autorelease從而平衡它的引用計(jì)數(shù)器,避免內(nèi)存泄露,。當(dāng)然我們也可以在使用代碼塊之后將其手動(dòng)釋放,,不過這 就不符合誰創(chuàng)建誰釋放的原則了。你不會(huì)經(jīng)常需要對(duì)代碼塊進(jìn)行copy操作,,但是如果是上面所講的情況你就需要了,,這點(diǎn)請(qǐng)留意。
將所學(xué)的整合在一起
    那么,,讓我們來把所學(xué)的東西整合為一個(gè)更實(shí)際點(diǎn)的例子,。假設(shè)我們要設(shè)計(jì)一個(gè)簡單的播放電影的類,這個(gè)類的使用者希望電影播放完之后能夠接受一個(gè)用于展現(xiàn)應(yīng)用特定邏輯的回調(diào),。前面已經(jīng)證明代碼塊是處理回調(diào)很方便的方法,。
讓我們開始寫代碼吧,從一個(gè)使用這個(gè)類的開發(fā)人員的角度來寫:
MoviePlayer *player = 
    [[MoviePlayer alloc] initWithCallback:^(NSString *title) {
        NSLog(@"Hope you enjoyed %@", title);
}];
 
[player playMovie:@"Inception"];
    可 以看出我們需要MoviePlayer類,,他有兩個(gè)方法:initWithCallback:和playMovie:,,初始化的時(shí)候接受一個(gè)代碼塊,然后 將它保存起來,,在執(zhí)行playMovie:方法結(jié)束后再調(diào)用代碼塊,。這個(gè)代碼塊需要一個(gè)參數(shù)(電影的名字),返回void類型,。我們對(duì)回調(diào)的代碼塊類型使 用typedef,,使用property來保存代碼塊變量。記住,,代碼塊是對(duì)象,,你可以像實(shí)例變量或?qū)傩砸粯邮褂盟_@里我們將它當(dāng)作屬性使用,。下面是 MoviePlayer.h:
typedef void (^MoviePlayerCallbackBlock)(NSString *);
 
@interface MoviePlayer : NSObject {
}
 
@property (nonatomic, copy) MoviePlayerCallbackBlock callbackBlock;
 
- (id)initWithCallback:(MoviePlayerCallbackBlock)block; 
- (void)playMovie:(NSString *)title;
 
@end
下面是MoviePlayer.m:
#import "MoviePlayer.h"
 
@implementation MoviePlayer
 
@synthesize callbackBlock;
 
- (id)initWithCallback:(MoviePlayerCallbackBlock)block {
    if (self = [super init]) {
        self.callbackBlock = block;
    }
    return self;
}
 
- (void)playMovie:(NSString *)title {
    // play the movie
    self.callbackBlock(title);
}
 
- (void)dealloc {
    [callbackBlock release];
    [super dealloc];
}
 
@end
    在 initWithCallback:方法中將要使用的代碼塊聲明為callbackBlock屬性,。由于屬性被聲明為了copy方式,代碼塊會(huì)自動(dòng)進(jìn)行 copy操作,從而將其移到堆中,。當(dāng)playMovie:方法調(diào)用時(shí),,我們傳入電影的名字作為參數(shù)來調(diào)用代碼塊。
    現(xiàn)在我們假設(shè)一個(gè)開發(fā)人員要在程序中使用我們的MoviePlayer類來管理一組你打算觀看的電影,。當(dāng)你看完一部電影之后,這部電影就會(huì)從組中移除,。下面是一個(gè)簡單的實(shí)現(xiàn),,使用了閉包:
NSMutableArray *movieQueue = 
    [NSMutableArray arrayWithObjects:@"Inception", 
                                     @"The Book of Eli", 
                                     @"Iron Man 2", 
                                     nil];
 
MoviePlayer *player = 
    [[MoviePlayer alloc] initWithCallback:^(NSString *title) {
        [movieQueue removeObject:title];
}];
 
for (NSString *title in [NSArray arrayWithArray:movieQueue]) {
    [player playMovie:title];
};

    請(qǐng) 注意代碼塊使用了本地變量movieQueue,它會(huì)成為代碼塊狀態(tài)的一部分。當(dāng)代碼塊被調(diào)用,,就會(huì)從數(shù)組movieQueue中移除一個(gè)電影,,盡管此時(shí) 數(shù)組是在代碼塊作用域之外的。當(dāng)所有的電影播放完成之后,,movieQueue將會(huì)是一個(gè)空數(shù)組,。下面是一些需要提及的重要事情:
1、movieQueue變量是一個(gè)數(shù)組指針,,我們不能修改它的指向,。我們修改的是它指向的內(nèi)容,因此不需要使用_block修飾,。
2,、為了迭代movieQueue數(shù)組,我們需要?jiǎng)?chuàng)建一個(gè)它的copy,,否則如果我們直接使用movieQueue數(shù)組,,就會(huì)出現(xiàn)在迭代數(shù)組的同事還在移除它的元素,這會(huì)引起異常,。
3,、如果不使用代碼塊,我們可以聲明一個(gè)協(xié)議,,寫一個(gè)代理類,,并注冊(cè)這個(gè)代理作為回調(diào)。很明顯該例子使用內(nèi)聯(lián)代碼塊更方便,。
4,、在不改變MoviePlayer類的前提下可以給他增加新功能。比如另一個(gè)開發(fā)者可以在看完一部電影后將其分享到twitter或?qū)﹄娪斑M(jìn)行評(píng)價(jià)等,。

翻譯自 http:///blog/2010/7/28/ios4-blocks-1

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多