第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/oreilly/a283/ch05.pdf ·...

10
第五章 共時性 當有兩個或更多個任務(tasks)同時執行,就能達到所謂的共時性(concurrency)。即 使是在單 CPU 的平台上,現代的作業系統也有能力以共時(共用時間)的方式執行任 務,它們將 CPU 的處理時間切成特定的時間片段(time slice)分給每個任務,以達到 共時的效果。舉例來說,如果 1 秒要執行 10 個具有相同優先序(priority)的任務,作 業系統就會將 1000 毫秒(milliseconds,即 1 秒)除以 10(個任務),然後賦予每個任 100 毫秒的 CPU 時間,這表示這些任務會在同一秒內執行完畢,使它們看起來好像 是同時被執行。 然而,隨著科技的演進,現在我們的 CPU 幾乎都有一個以上的核心(core),這表示現 代的 CPU 真的有辦法同時執行多個任務,作業系統會分配(dispatch)任務給 CPU,然 後等候它們執行完畢,就這麼簡單! Grand Central Dispatch,或簡稱 GCD,是搭配 block 物件一起使用的低階 C APIGCD 真正用途是分配任務給多個核心,並且不用你,也就是程式設計師,來擔心哪個核心執 行哪項任務。多核心的 Mac OS X 裝置,包括筆記型電腦(laptops),已經存在於市面 上好一段時間了。iPad 2 等後續的裝置引入多核心 CPU 之後,程式設計師也可以為 iOS 撰寫能夠善用多核心、具有多執行緒(multithreaded)、令人驚嘆的 apps 了。 GCD 的核心是 dispatch queues(分發佇列)。Dispatch queues,如我們很快就會看到 的,是在宿主作業系統(host operating system,不管是 iOS Mac OS X)上由 GCD 所管理的 pools of threads(執行緒池)。你不會直接操作這些執行緒(threads,或稱 「線程」),你僅能接觸到 dispatch queues,你得將任務tasks分發給這些 queues(佇 列),並要求這些 queues 調用(invoke)你的任務。GCD 提供了幾種任務執行方式:同 步(synchronously)、非同步(synchronously)、在特定延遲後(after a certain delay執行等等。 325

Upload: others

Post on 26-May-2020

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

第五章

共時性

當有兩個或更多個任務(tasks)同時執行,就能達到所謂的共時性(concurrency)。即使是在單 CPU的平台上,現代的作業系統也有能力以共時(共用時間)的方式執行任務,它們將 CPU的處理時間切成特定的時間片段(time slice)分給每個任務,以達到共時的效果。舉例來說,如果 1秒要執行 10個具有相同優先序(priority)的任務,作業系統就會將 1000毫秒(milliseconds,即 1秒)除以 10(個任務),然後賦予每個任務 100毫秒的 CPU時間,這表示這些任務會在同一秒內執行完畢,使它們看起來好像是同時被執行。

然而,隨著科技的演進,現在我們的 CPU幾乎都有一個以上的核心(core),這表示現代的 CPU真的有辦法同時執行多個任務,作業系統會分配(dispatch)任務給 CPU,然後等候它們執行完畢,就這麼簡單!

Grand Central Dispatch,或簡稱 GCD,是搭配 block物件一起使用的低階 C API。GCD真正用途是分配任務給多個核心,並且不用你,也就是程式設計師,來擔心哪個核心執

行哪項任務。多核心的 Mac OS X裝置,包括筆記型電腦(laptops),已經存在於市面上好一段時間了。iPad 2等後續的裝置引入多核心 CPU之後,程式設計師也可以為 iOS撰寫能夠善用多核心、具有多執行緒(multithreaded)、令人驚嘆的 apps了。

GCD 的核心是 dispatch queues(分發佇列)。Dispatch queues,如我們很快就會看到的,是在宿主作業系統(host operating system,不管是 iOS或 Mac OS X)上由 GCD所管理的 pools of threads(執行緒池)。你不會直接操作這些執行緒(threads,或稱「線程」),你僅能接觸到 dispatch queues,你得將任務(tasks)分發給這些 queues(佇列),並要求這些 queues調用(invoke)你的任務。GCD提供了幾種任務執行方式:同步(synchronously)、非同步(synchronously)、在特定延遲後(after a certain delay)執行等等。

325

Page 2: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

328 | 第五章

要在你的 apps中使用 GCD,你不必為你的專案引入(import)任何特別的程式庫,Apple已經將 GCD整合至各個框架中,包括 Core Foundation與 Cocoa/Cocoa Touch。GCD中所有方法與資料型別(data types)都以 dispatch_關鍵字來開頭,舉例來說,

dispatch_async讓你分配一項任務到一個 queue中以非同步執行;而 dispatch_after讓你在給定的時間延遲後執行一個 block(區塊)的程式碼。

在 GCD和 operations(作業)的概念出現之前,程式設計師若想要平行地(in parallel)執行任務,就得建立他們自己的執行緒。舉例來說,iOS開發者可能會建立一個類似下面這樣的執行緒,來執行一個 operation(作業)1000次:

- (void) doCalculation{ /*在此進行你的計算 */}

- (void) calculationThreadEntry{

@autoreleasepool { NSUInteger counter = 0; while ([[NSThread currentThread] isCancelled] == NO){ [self doCalculation]; counter++; if (counter >= 1000){ break; } } }

}

- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

/*起始執行緒 */ [NSThread detachNewThreadSelector:@selector(calculationThreadEntry) toTarget:self withObject:nil];

self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;}

326

Page 3: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

共時性 | 329

程式設計師必須手動起始這個執行緒,然後為這個執行緒建立所需的結構(進入點 entry point、自動釋放池 autorelease pool,以及執行緒的主迴圈)。當我們使用 GCD來撰寫同樣的程式碼,我們需要做的就少了很多,我們只需將程式碼放入一個 block物件,然後將這個 block物件發配給 GCD執行。我們可以決定這段程式碼要在主執行緒中執行,或是在其他執行緒中執行。這裡有個例子:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

size_t numberOfIterations = 1000;

dispatch_async(queue, ^(void) { dispatch_apply(numberOfIterations, queue, ^(size_t iteration){ /*在此執行作業 */ });});

在這一章中,你會學到有關 GCD所需知道的一切,以及如何使用它來為 iOS與 Mac OS X撰寫現代的多執行緒 apps,讓它們在多核心裝置(例如 iPad 2)上發揮出驚人的效能。

我們會常常用到 dispatch queues(分發佇列),所以請確切的了解它們背後的概念。Dispatch queues有三種:

Main queue(主佇列)

這個 queue會在主執行緒(main thread)上執行它所有的任務(tasks),Cocoa與Cocoa Touch就要求程式設計師必須在 main thread中呼叫所有 UI相關的方法。請使用 dispatch_get_main_queue函式來取得操作 main thread用的 handle(權柄)。

Concurrent queues(共時佇列)

你 可 以 從 GCD 那 取 得 這 些 queues 來 執 行 同 步(synchronous) 或 非 同 步(asynchronous)的任務。多個 concurrent queues能夠平行進行多項任務,不費吹灰之力,更棒的是,你不用自行管理執行緒了!請使用 dispatch_get_global_queue函式來取得一個 concurrent queue的 handle。

Serial queues(循序佇列)

這種 queues,不管你提送給他們的是同步或非同步任務,它們一定都會以 first-in-first-out(FIFO,先進先出)的方式來執行它們的任務,這表示它們一次只能執行一個 block物件。不過它們並非在 main thread中執行,因此,如果你要執行的一系列任務必須以嚴格的順序進行,但你不希望阻斷 main thread的執行,你就可以使用這一種 queues。請使用 dispatch_queue_create函式來建立一個 serial queue,一旦這個 queue使用完畢,你必須使用 dispatch_release函式來釋放它。

327

Page 4: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

330 | 第五章

在你應用程式生命期(lifetime)的任何一個時間點,你都能夠同時使用多個 dispatch queues。你的系統只會有一個 main thread,不過你可以為你 app所需的任何功能建立任意多個(當然是在合理範圍內)serial dispatch queues。你也可以取得多個 concurrent queues 然後將你的任務分發給它們。任務(tasks)可以兩種形式提送給 dispatch queues:block物件(block objects)或是 C函式(C functions),如我們將會在 Recipe 5.4中見到的。

Block(區塊)物件是程式碼的包裹(packages of code),在 Objective-C中通常以方法(method)的形式出現。Block物件,連同 Grand Central Dispatch(GCD),建立了一個和諧的環境,在其中你能夠製作出高效能、多執行緒的 iOS與 Mac OS X apps。你可能會問,block物件與 GCD有什麼特別之處呢?很簡單:不用再去管執行緒了!你所需要做的只是將你的程式碼放在 block物件中,然後請 GCD替你管理之後的執行工作。

或許 block物件與傳統的函式指標(function pointers)之間最大的差異

是前者會將在 block物件內所存取到的區域變數(local variables)的值

拷貝一份,使用的時候就用這份複製品。如果這些變數的值在 block物件

的範疇(scope)外被修改了,你也不必擔心 block物件自己保存的那些

值會受到影響。我們很快就會更深入討論這個議題。

Objective-C中的 block物件在程式設計領域被稱為 first-class objects(一級物件)。這表

示你能夠動態地建置程式碼、將一個 block物件傳入一個方法作為參數,或是從方法回傳一個 block物件,這些都能讓你在 runtime(執行期)更輕易地選擇你想做的事,並隨之變更程式的動作,更有甚者,block物件還可以藉由 GCD在個別的執行緒中執行。作為 Objective-C的物件,block物件可被當成普通物件來對待,就像其他物件一樣。

Block物件有時會被稱為 closures(閉包)。

Block物件的建構方式類似於傳統 C函式的建構方式,如我們會在 Recipe 5.1見到的。Block物件可以有回傳值(return values),也可以接受參數(parameters)。Block物件可在行內被定義(defined inline)或是被當成個別的程式碼區塊(block of code)來對待,類似 C的函式。如果是在行內被建立(created inline),block物件可存取的變數範疇(scope)會與被實作為個別程式碼區塊的 block物件大不相同。

GCD可以搭配 block物件使用。透過 GCD執行任務時,你能傳入一個 block物件,其程式碼可以被非同步執行,或是同步執行,這就要看你用的是 GCD的哪一個方法。因

328

Page 5: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

共時性 | 331

此,你可以建立一個 block物件,負責下載作為參數傳入給它的 URL,這一個 block物件隨後可被用在你 app的各個地方,進行同步或非同步的下載,取決於你執行它的方式,你不必讓這個 block物件本身能夠同步或非同步執行,你只需要透過同步或非同步的 GCD方法來呼叫它就行了,而這個 block物件就會如你預期的運作。

Block物件對撰寫 iOS與 OS X apps的程式設計師來說算是相當新的東西。實際上,block物件尚不如執行緒(threads)那般普及,或許是因為它們的語法(syntax)和純粹的 Objective-C方法比起來有些不同,也較為複雜。儘管如此,block物件的功能非常強大,Apple也正大力推動這項功能,想要將它們整合進 Apple的程式庫。你已經可以在某些類別中看到這些整合的結果,例如在 NSMutableArray類別中,程式設計師可以使用block物件來為陣列排序(sort)。

本章致力於探討 iOS與 Mac OS X apps中 block物件的建構與使用方式,以及如何使用GCD來將任務分發給作業系統、執行緒與計時器(timers)。我想要強調,要習慣 block物件的語法,唯一的方式就是自己動手撰寫幾個 block物件。你可以先看看本章的範例程式碼,然後試著去實作你自己的 block物件。

在此,你會學到 block物件的基礎知識,以及一些更進階的主題,例如 Grand Central Dispatch、Threads(執行緒)、Timers(計時器)、Operations(作業)與 Operation Queues(作業佇列)。在進入 Grand Central Dispatch之前,你會了解使用 block物件所需的一切知識。以我的經驗來說,學習 block物件最佳的方式是經由範例,所以你將會在此章中見到大量的例子。請記得一定要在 Xcode中實際試試這些範例,才能夠真正體會 block物件的語法。

Operations(作業)可以被配置來同步或非同步執行一個區塊的程式碼(a block of code)。你可以手動管理 operations或是將它們放在 operation queues(作業佇列)中,

自動進行共時性的作業,讓你不必去處理底層的執行緒管理工作。在此章中,你會看到

如何使用 operations與 operation queues,以及基本的執行緒與計時器,在應用程式中同步或非同步執行任務。

Cocoa提供了下列三種不同型別的 operations:

Block operations(區塊作業)

這能夠執行一或更多個 block物件。

Invocation operations(調用作業)

這能讓你調用(invoke)另一個現存物件中的方法。

329

Page 6: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

332 | 第五章

Plain operations(一般作業)

這些是一般的作業類別,需要衍生出子類別後才能夠使用。要被執行的程式碼必須

寫在 operation物件的 main方法內。

Operations, 如 前 面 提 過 的, 可 用 operation queues 來 管 理, 其 資 料 型 別 為NSOperationQueue。在實體化了上面提過的任何一種型別(block、invocation 或 plain operation)的 operation 之後,你就可以將它們加到一個 operation queue 中,讓那個queue來管理 operation。

一個 operation 物件可能會依存於其他的 operation 物件,而會被指示要等候一或更多個 operations 完成之後,才能執行與它關聯的任務。除非你新增了某種依存性(dependency),否則你沒有辦法控制 operations執行的順序,舉例來說,以某個特定順序將它們加到一個 queue中並不代表它們一定會以那種順序執行,儘管我們在此所用的術語是 queue(佇列)。

使用 operation queues與 operations時,有幾件重要的事要牢記在心:

y Operations,在預設的情況下,會在透過它們 start實體方法來起始它們的執行緒上執行。如果你希望 operation以非同步(asynchronously)運行,你就必須使用一個operation queue,或是衍生出 NSOperation的一個子類別,並在該 operation的 main實體方法上分離(detach)出一個新的執行緒。

y 一個 operation可以等候另一個 operation執行完畢之後,再起始(start)它自己。注意不要建立出相互依存(interdependent)的 operations,這種常見的錯誤被稱為deadlock(死結)。換句話說,如果 operation B已經依存於 operation A,就不要再讓 operation A依存於 operation B,這會使兩者都無止盡的等候下去,佔去記憶體,並可能使你的應用程式停止回應。

y Operations可以被取消(cancel),所以,如果你已經衍生出 NSOperation的子類別,建立了自訂的 operation物件,你必須在執行與那個 operation關聯的任務之前,使用 isCancelled實體方法來檢查該 operation是否已經被取消。舉例來說,如果你的operation的任務是每 20秒檢查檢查一次 Internet(網際網路)連線的可用性,它必須在每次開始檢查前,先呼叫 isCancelled實體方法,以確保這個動作之前沒有被取消過。如果一項 operation所需的時間不只幾秒鐘(例如下載檔案時),你應該在執行任務的同時,週期性的呼叫 isCancelled來進行檢查。

y Operation 物件在各個 key paths(鍵值路徑)上,例如 isFinished、isReady、

isExecutin,都符合 key-value observing(KVO)。我們會在後面的章節中討論 Key Value Coding與 Key Value Observing。

330

Page 7: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

共時性 | 333

y 如果你打算衍生出 NSOperation的子類別(subclass),並為 operation提供自訂的實作,你必須在該 operation的 main方法(會被 start方法呼叫)中建立你自己的autorelease pool(自動釋放池),我們會在本章後面深入討論。

y 一定要保存指向你所建立的 operation物件的參考(reference)。Operation queues的共時性本質可能會使你無法在一個 operation被加到 queue之後,取得它的參考。

執行緒(threads)與計時器(timers)也是物件,是 NSObject的子類別。產生一個執行緒所需的工作比建立計時器還要多,而設立一個執行緒迴圈(thread loop)比監聽一個選擇器(selector)上的計時器是否觸發,還要來得困難。當一個應用程式在 iOS上執行,作業系統至少會為該應用程式建立一個執行緒,稱為主執行緒(main thread)。每個執行緒與計時器都必須被加到一個執行迴圈(run loop)中,一個執行迴圈,顧名思義,是在其執行期間可能會有不同事件發生的一個迴圈,例如計時器被觸發或是一個執

行緒開始執行。執行迴圈的詳細說明已超出本章的範圍,不過我們不時會在其他訣竅

(recipes)中提到它。

你可以把一個執行迴圈想成是一種迴圈,它具有起始點、完成條件,以及一系列要在其

生命期(lifetime)間處理的事件(events)。一個執行緒或計時器會被附加(attach)到一個執行迴圈上,而實際上它們也必須搭配執行迴圈才能運作。

一個應用程式的主執行緒就是負責處理 UI事件的執行緒。如果你在主執行緒上執行一個需要長時間運行的任務,你會發現你的應用程式 UI變得沒有回應,或是回應很慢。要避免這種事情發生,你能夠建立個別的執行緒或計時器,讓它們各自去執行它們自己

的任務(即便是需要耗費長時間的任務),如此一來就不會阻斷主執行緒的運行。

5.1 建構 Block物件

問題

你想要撰寫你自己的 block物件,或是透過 block物件來使用 iOS SDK中的類別。

解決方案

你只需要理解 block物件與傳統的 C函式之間的基本語法差異,這些差異會在討論段落中詳加說明。

331

Page 8: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

334 | 第五章

討論

Block物件可以是 inline(直接寫在程式碼行內)的,或是做為獨立的程式碼區塊(blocks of code),讓我們從後面這種開始說明。假設你有一個 Objective-C方法,它會接受兩個型別為 NSInteger的整數值,並將它們相減以回傳兩個值的差,這個差值的型別也是

NSInteger:

- (NSInteger) subtract:(NSInteger)paramValue from:(NSInteger)paramFrom{

return paramFrom - paramValue;

}

這很簡單,不是嗎?現在,讓我們將這段 Objective-C程式碼轉譯為純粹的 C函式,提供相同的功能,如此我們才能更進一步學習 block物件的語法:

NSInteger subtract(NSInteger paramValue, NSInteger paramFrom){

return paramFrom - paramValue;

}

你會發現,這個 C函式所用的語法與等效的 Objective-C程式碼比起來相當的不同。現在讓我們看一下我們如何撰寫相同功能的 block物件:

NSInteger (^subtract)(NSInteger, NSInteger) = ^(NSInteger paramValue, NSInteger paramFrom){

return paramFrom - paramValue;

};

在我深入 block物件的語法細節之前,讓我再多展示幾個範例。假設我們有一個 C函式,它接受一個型別為 NSUInteger(無號整數)的參數,並將它回傳為型別為 NSString的字串,這裡是它的 C實作:

NSString* intToString (NSUInteger paramInteger){

return [NSString stringWithFormat:@"%lu", (unsigned long)paramInteger];

}

332

Page 9: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

共時性 | 335

要深入了解如何在 Objective-C 中使用獨立於系統的格式限定符

(system-independent format specifiers)來格式化字串,請參考 Apple

網站上 iOS Developer Library中的 String Programming Guide((https://

developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Strings/

Articles/formatSpecifiers.html)。

與這個 C函式等效的 block物件展示於範例 5-1。

範例 5-1. 定義為函式的 block 物件範例

NSString* (^intToString)(NSUInteger) = ^(NSUInteger paramInteger){ NSString *result = [NSString stringWithFormat:@"%lu", (unsigned long)paramInteger];

return result;};

一個獨立的 block物件最簡單的形式是一個回傳 void並且不接受任何參數的 block物件:

void (^simpleBlock)(void) = ^{ /*在此實作這個 block物件 */};

Block物件的調用(invoke)方式與 C函式完全相同。如果它們有參數,你就像傳參數給 C函式一樣傳入參數給它們,而回傳值取回的方式也跟取回 C函式回傳值的方式一模一樣。這裡有個例子:

NSString* (^intToString)(NSUInteger) = ^(NSUInteger paramInteger){ NSString *result = [NSString stringWithFormat:@"%lu", (unsigned long)paramInteger]; return result;};

- (void) callIntToString{

NSString *string = intToString(10); NSLog(@"string = %@", string);

}

callIntToString這個 Objective-C方法藉由傳入 10這個值作為唯一的參數來呼叫 block物件 intToString,並將這個 block物件的回傳值放到區域變數 string中。

Page 10: 第五章 共時性 - 碁峰資訊epaper.gotop.com.tw/Oreilly/A283/ch05.pdf · 執行任務,就得建立他們自己的執行緒。舉例來說,iOS 開發者可能會建立一個類似下

336 | 第五章

現在我們已經知道如何撰寫作為獨立程式碼區塊的 block物件,現在就讓我們看一下如何把 block物件當作參數傳給 Objective-C方法。我們會需要進行一點抽象思考,以了解下面範例的目的。

假設我們有一個 Objective-C 方法,此方法接受一個整數,並對它進行某種轉換(transformation),實際上進行的是何種轉換,則要看程式其他部分所發生的事情而定。我們知道會有一個整數作為輸入,而輸出則是一個字串,不過我們會將實際的轉換工作

留給一個 block物件去進行,而這個方法每次執行時,都能夠使用不同的 block物件。因此,這個方法會接受兩個參數:一個整數,以及用來對這個整數進行轉換工作的一個

block物件。

我們會用先前在範例 5-1中實作的那個 intToString block物件來作為這裡的 block物件參數。現在我們需要一個 Objective-C 方法,它接受一個無號整數與一個 block 物件作為參數,無號整數參數的部分很容易,但我們要如何告知這個方法它必須接受一

個與 intToString block 物件同樣型別的 block 物件呢?首先我們用 typedef來定義

intToString block物件的 signature(特徵式,或稱「簽名式」),這會告知編譯器這種block物件應該接受那些參數:

typedef NSString* (^IntToStringConverter)(NSUInteger paramInteger);

這一段 typedef程式碼告訴編譯器,接受一個整數參數,並且回傳一個字串的 block物件可由 IntToStringConverter這個識別字(identifier)來表示。接著我們就可以開始撰寫這個接受一個整數,以及一個型別為 IntToStringConverter的 block物件作為參數的Objective-C方法:

- (NSString *) convertIntToString:(NSUInteger)paramInteger usingBlockObject:(IntToStringConverter)paramBlockObject{

return paramBlockObject(paramInteger);

}

現在我們就可以用我們所選擇的 block物件來呼叫這個 convertIntToString:方法(範例 5-2)。

範例 5-2. 在另外一個方法中呼叫這個 block 物件

- (void) doTheConversion{

NSString *result = [self convertIntToString:123 usingBlockObject:intToString];

NSLog(@"result = %@", result);

}

333