iOS--代碼塊

iOS開發-代碼塊的使用
2012年04月13日 ⁄ Iphone技巧 ⁄ 共 7288字 ⁄ 暫無評論

iOS4引入了一個新特性,支持代碼塊的使用,這將從根本上改變你的編程方式。代碼塊是對C語言的一個擴展,因此在Objective-C中完全支持。如果你學過Ruby,Python或Lisp編程語言,那麼你肯定知道代碼塊的強大之處。簡單的說,你可以通過代碼塊封裝一組代碼語句並將其當作一個對象。代碼塊的使用是一種新的編碼風格,可以讓你運用自如的使用iOS4中新增API。
我們先來看兩個在iOS4中使用代碼塊的例子(你很有可能已經見過):view animations 和enumeration
使用代碼塊的例子
第一個例子,假設我們創建一個紙牌遊戲,需要展現紙牌被派發到玩家面前的動畫效果。幸運的是通過UIKit框架可以很容易的實現一個動畫效果。但是最終是什麼樣的動畫是由你的程序決定的。你可以在代碼塊中指定動畫的內容然後再將代碼塊傳給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);
}
];

當這個動畫代碼塊執行時,我們的紙牌會展現三種方式的動畫:改變它的alpha值從而淡入顯示,改變它的位置到右下角(玩家的位置),以及自轉180度(爲了使其效果更好)。
第二個代碼塊的例子是迭代一個紙牌的集合,並打印其名字和在集合裏的索引值。
你可以通過使用for循環來達到目的,但是在iOS4中NSArray類有一個使用了代碼塊的方便方法: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);
}];

這個代碼塊使用了三個參數:數組中的一個對象,該對象的索引,以及一個標識迭代是否結束的標誌。我們稍候再對其進一步探討。enumerateObjectsUsingBlock: 這個方法會將集合中的每一個元素傳入相應的參數並調用代碼塊中的方法。
因此在你的mac和iOS程序中使用代碼塊的優勢是:它允許你附加任意的代碼到蘋果官方提供的方法上。儘管在概念上與代理相似,但是在方法中使用簡短的內聯代碼塊往往更加方便,更加優雅。
這是一個好的開始,但重要的是要明白它內部的處理。當我學習新東西的時候,我喜歡先將其分爲一個個簡單的部分,瞭解它們如何工作,然後再將它們組裝到一塊,這樣我會對自己寫的代碼以及快速解決出現的問題充滿信心。因此,讓我們先回頭學習下如何聲明和調用簡單的代碼塊。
代碼塊的基本概念
一個代碼塊可以簡單看作是一組可執行的代碼。例如,下面是一個打印當前日期和時間的代碼塊:
^ {
NSDate *date = [NSDate date];
NSLog(@"The date and time is %@", date);
};
插入符號(^)聲明一個代碼塊的開始,一對大括號{}構成了代碼塊的體部。你可以認爲代碼塊與一個匿名函數類似。那麼,如果是一個匿名的函數,我們該怎麼調用這個代碼塊呢?最常見使用代碼塊的方式是將其傳入方法中供方法回調,就像之前我們已經見到了view animations 和enumeration。另一種使用代碼塊的方式是將其賦予代碼塊變量,然後可使用該變量來直接調用代碼塊。以下是如何聲明我們的代碼塊並將它賦予代碼塊變量now:
void (^now)(void) = ^ {
NSDate *date = [NSDate date];
NSLog(@"The date and time is %@", date);
};
聲明一個塊變量的語法需要一些時間適應,這纔有趣。如果你使用過函數指針,代碼塊變量與其類似。在上面代碼等號右邊是我們已經介紹過的代碼塊。等號左邊我們聲明瞭一個代碼塊變量now。
[attachment=25059]
代碼塊變量之前有^符號並被小括號包着,代碼塊變量有類型定義的。因此,上圖中的now變量可以應用任何無參,無返回值的代碼塊。我們之前聲明的代碼塊符合這要求,,所以我們可以放心的把它分配給now變量。
只要有一個代碼塊變量,並在其作用域範圍內,我們就可以像調用函數一樣來調用它。下面是如何調用我們的代碼塊:
now();
你可以在C函數或者Objective-c方法中聲明代碼塊變量,然後在同一作用域內調用它,就像我們前面說明那樣。當代碼塊執行時,它打印當前的日期和時間。目前爲止,進展順利。
代碼塊是閉包
如果這就是代碼塊的全部的話,那麼他與函數是完全相同的。但事實是代碼塊不僅僅是一組可執行的代碼。代碼塊能夠捕捉到已聲明的同一作用域內的變量,同時由於代碼塊是閉包,在代碼塊聲明時就將使用的變量包含到了代碼塊範圍內。爲了說明這一點,讓我們改變一下前面的例子,將日期的初始化移到代碼塊之外。
NSDate *date = [NSDate date];

void (^now)(void) = ^ {
NSLog(@"The date and time is %@", date);
};

now();
當你第一次調用這個代碼塊的時候,它與我們之前的版本結果完全一致:打印當前的日期和時間。但是當我們改變日期後再調用代碼塊,那麼就會有顯著的不同了,
sleep(5);

date = [NSDate date];

now();
儘管我們在調用代碼塊之前改變了日期,但是當代碼塊調用時仍然打印的是之前的日期和時間。就像是日期在代碼塊聲明時停頓了一樣。爲什麼會這樣呢,當程序執行到代碼塊的聲明時,代碼塊對同一作用域並且塊內用到的變量做一個只讀的備份。你可以認爲變量在代碼塊內被凍結了。因此,不論何時當代碼塊被調用時,立即調用或5秒鐘之後,只要在程序退出之前,它都是打印最初的日期和時間。
事實上,上面那個展示代碼塊是閉包的例子並不十分完善,畢竟,你可以將日期作爲一個參數傳入到代碼塊中(下面講解)。但是當你將代碼塊在不同方法間傳遞時閉包的特性就會變得十分有用,因爲它裏面的變量是保持不變的。
代碼塊參數
就像函數一樣,代碼塊可以傳入參數和返回結果。例如,我們想要一個能夠返回指定數的三倍的代碼塊,下面是實現的代碼塊:
^(int number) {
return number * 3;
};
爲代碼塊聲明一個變量triple,如下:
int (^triple)(int) = ^(int number) {
return number * 3;
};
上面說過,我們需要熟悉等號左邊聲明代碼塊變量的語法。現在讓我們從左到右分開來說明:
[attachment=25060]
最左邊的int是返回值類型,中間是小括號包圍插入符號^及代碼塊變量的名字,最後又一個小括號,包圍着參數的類型(上面例子中只有一個int參數)。等號右邊的代碼塊聲明必須符合左側的定義。有一點要說明的是,爲了方便,可以不聲明代碼塊的返回類型,編譯器會從返回語句中做出判斷。
要調用這個代碼塊,你需要傳入一個需要乘3的參數,並接受返回值,像這樣:
int result = triple(2);

下面你將知道如何聲明並創建一個需要兩個int型參數,將它們相乘然後返回結果的代碼塊:
int (^multiply)(int, int) = ^(int x, int y) {
return x * y;
};

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

聲明代碼塊變量使我們有機會探討代碼塊類型以及如何調用。代碼塊變量類似函數指針,調用代碼塊與調用函數相似。不同於函數指針的是,代碼塊實際上是Objective-C對象,這意味着我們可以像對象一樣傳遞它們。
調用代碼塊的方法
在實際中,代碼塊經常被作爲參數傳入方法中供其回調。當把代碼塊作爲一個參數時,相比分配一個代碼塊變量,更通常的做法是作爲內聯代碼塊。例如,我們之前看到的例子:view animations 和enumeration。
蘋果官方已經增加了一些使用代碼塊的方法到他們的框架中。你也可以寫一些使用代碼塊的API了。例如,我們要創建一個Worker類的使用代碼塊的類方法,該方法重複調用代碼塊指定的次數,並處理代碼塊每次返回的結果。下面是我們使用內聯代碼塊調用這個方法,代碼塊負責返回1到10的每個數的三倍。
[Worker repeat:10 withBlock:^(int number) {
return number * 3;
}];

這個方法可以將任何接受一個int型參數並返回一個int型結果的代碼塊作爲參數,如果想得到數字的二倍,只需要改變傳入方法的代碼塊。

 

=================================================================

 

1.聲明和使用代碼塊
一般用^操作符聲明一個塊變量,並作爲塊的開始符。而塊的本身用{}包括起來,就像下面那樣。

  1. int multiplier = 7;
  2. int (^myBlock)(int) = ^(int num) {
  3. return num * multiplier;
  4. };

複製代碼

下面的圖是詳細的講解:

其實意思就是前半句聲明瞭一個名字爲myBlock的代碼塊,有一個int類型的參數,並返回一個int類型的值;後面的半句就是一個塊的定義,然後賦值給myBlock。
如果我們像上面那樣,聲明一個塊像一個變量一樣,我們就可以像使用函數一樣使用它,如下:

  1. int multiplier = 7;
  2. int (^myBlock)(int) = ^(int num) {
  3. return num * multiplier;
  4. };
  5. printf("%d", myBlock(3));
  6. // prints "21"

複製代碼

2.直接使用block
在大多數情況下,我們不需要去聲明一個塊變量,我們直接寫一個簡單的代碼塊作爲參數傳遞就行。下面的代碼函數qsort_b的第三個參數就是一個代碼塊。

  1. char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
  2. qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
  3. char *left = *(char **)l;
  4. char *right = *(char **)r;
  5. return strncmp(left, right, 1);
  6. });
  7. // myCharacters is now { "Charles Condomine", "George", "TomJohn" }

複製代碼

一些cocoa frameworks的方法採用一個block作爲一個參數,典型的是對一個集合對象進行操作,或者是在一個操作完成之後使用回調。下面的例子是NSArray類的方法sortedArrayUsingComparator:怎樣使用一個block。此方法使用一個block作爲一個參數。

  1. NSArray *stringsArray = [NSArray arrayWithObjects:
  2. @"string 1",
  3. @"String 21",
  4. @"string 12",
  5. @"String 11",
  6. @"String 02", nil];
  7. static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
  8. NSWidthInsensitiveSearch | NSForcedOrderingSearch;
  9. NSLocale *currentLocale = [NSLocale currentLocale];
  10. NSComparator finderSortBlock = ^(id string1, id string2) {
  11. NSRange string1Range = NSMakeRange(0, [string1 length]);
  12. return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
  13. };
  14. NSArray *finderSortArray = [stringsArraysortedArrayUsingComparator:finderSortBlock];
  15. NSLog(@"finderSortArray: %@", finderSortArray);

複製代碼

block的一個強大的功能是可以修改同一作用雨的變量,我們只需要在變量的前面加上一個_block標識符。下面的例子和上面的相同,只是添加功能用於記錄相同元素的個數。

  1. NSArray *stringsArray = [NSArray arrayWithObjects:
  2. @"string 1",
  3. @"String 21", // <-
  4. @"string 12",
  5. @"String 11",
  6. @"Strîng 21", // <-
  7. @"Striñg 21", // <-
  8. @"String 02", nil];
  9. NSLocale *currentLocale = [NSLocale currentLocale];
  10. __block NSUInteger orderedSameCount = 0;
  11. NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {
  12. NSRange string1Range = NSMakeRange(0, [string1 length]);
  13. NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];
  14. if (comparisonResult == NSOrderedSame) {
  15. orderedSameCount++;
  16. }
  17. return comparisonResult;
  18. }];
  19. NSLog(@"diacriticInsensitiveSortArray: %@", diacriticInsensitiveSortArray);
  20. NSLog(@"orderedSameCount: %d", orderedSameCount);

複製代碼


3.block變量的聲明
block的聲明和函數指針差不多,只是把*改爲了^

  1. void (^blockReturningVoidWithVoidArgument)(void);
  2. int (^blockReturningIntWithIntAndCharArguments)(int, char);
  3. void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);

複製代碼

也可以使用typedef去聲明block,方便以後使用,如下:

  1. typedef float (^MyBlockType)(float, float);
  2. MyBlockType myFirstBlock = // ... ;
  3. MyBlockType mySecondBlock = // ... ;

複製代碼

4.變量的作用域對於其在塊中的影響:

  1. _block int x = 123; // x lives in block storage
  2. void (^printXAndY)(int) = ^(int y) {
  3. x = x + y;
  4. printf("%d %dn", x, y);
  5. };
  6. printXAndY(456); // prints: 579 456
  7. // x is now 579

複製代碼

  1. extern NSInteger CounterGlobal;
  2. static NSInteger CounterStatic;
  3. {
  4. NSInteger localCounter = 42;
  5. __block char localCharacter;
  6. void (^aBlock)(void) = ^(void) {
  7. ++CounterGlobal;
  8. ++CounterStatic;
  9. CounterGlobal = localCounter; // localCounter fixed at block creation
  10. localCharacter = 'a'; // sets localCharacter in enclosing scope
  11. };
  12. ++localCounter; // unseen by the block
  13. localCharacter = 'b';
  14. aBlock(); // execute the block
  15. // localCharacter now 'a'
  16. }

複製代碼

5.使用blocks
(1)調用一個聲明好的block

  1. int (^oneFrom)(int) = ^(int anInt) {
  2. return anInt - 1;
  3. };
  4. printf("1 from 10 is %d", oneFrom(10));
  5. // Prints "1 from 10 is 9"
  6. float (^distanceTraveled) (float, float, float) =
  7. ^(float startingSpeed, float acceleration, float time) {
  8. float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
  9. return distance;
  10. };
  11. float howFar = distanceTraveled(0.0, 9.8, 1.0);
  12. // howFar = 4.9

複製代碼

發佈了16 篇原創文章 · 獲贊 8 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章