[心得] 探討Objective-C Block (part 1)

看板MacDev作者 (畢業了..@@")時間14年前 (2011/08/30 23:05), 編輯推噓1(100)
留言1則, 1人參與, 最新討論串1/1
Blog版 http://popcornylu.blogspot.com/2011/08/objective-c-block.html 在ios4推出後,出現了一個新的語言功能block。 這個功能其實有接觸多種程式語言的話, 會知道其實這個功能不算是Objctive-C特有的 別的語言可能叫做Lambda或是Closure, 這東西主要的特性是他除了是function外, 但是更紀錄了此function外部的環境。 也許有點抽象,我先舉個javascript的例子 var i = 2; var func = function(){ i = i * i;}; func(); // than i = 4 func(); // than i = 16 我們在中間定義一個function並且指派到func這個變數, 而在這邊可以用i這個外面的變數,因為在javascript中這樣的定義的就是一個closure, 他會記住外面有i這個變數, 所以之後我們連續呼叫兩次func, 則可以得到i是16這個結果 這個跟objective-c的block,或是別的語言的lambda,closure都是一樣的東西 相關的資料可以參考 http://goo.gl/aNd5 (Clousre - wikipedia) 裡面的介紹。 回到objective-c的block,當然這個好物當然要好好的利用一下, 這個東西最吸引人的地方就是可以帶不定數量的變數到function當中, 因為你只要環境中可以取得的都可以在function中使用 並且因為block(或是其他語言的closure)通常使用上都是anonymous function, 直接藏身在你的code當中, 因此可以讓程式碼更加精簡。 但是我覺得剛開始用block的時候,有時候會有知其然而不知其所以然的情況。 尤其是Objective-c最令人頭疼的記憶體管理的部份在block中更是複雜, 如果你不知道你的物件怎麼在block中去使用那會是一件危險的事情, 所以這個主題算是經驗分享文,來分享我現在對block的認知, 而當然ios有很多官方的API都支援block, 最有名的就是Grand Central Dispatch (GCD) 但是這篇不會介紹GCD, 我們把內容專注在block這個語言功能。 在講block怎麼用之前,容我先介紹C的function跟function pointer, 我認為在學block之前要有這個基本的認識比較好。 一來是block跟funtion有點相似,但是又有點不一樣, 知道function再來看block我覺得會比較透徹的瞭解 二來它們兩個定義的部份真的很像, 但是實作一個function跟block卻大不相同, 仔細想想兩個的差異絕對會對block更有深刻的感覺。 首先function相信大家都會實作function,這再簡單不過了 int plusone(int a) { return a+1; } 上面這裡定義了一個傳入參數是int回傳是int的function,這應該不用解釋太多了。 那function pointer呢? 可能開始有些人不是那麼熟悉了... int ooxx() { typedef int (*myfunc_type)(int a); myfunc_type myfunc = plusone; NSLog(@"return value = %d", myfunc(5)); } 這邊開始就要解釋了 第一行是我定義一個myfunc_type這個function pointer type, 而這個function type是傳入是int,回傳是int 而第二行是定義一個myfunc_type的變數,我讓他指到我們剛剛定義的plusone 而第三行我們就是去呼叫這個function pointer, 則他就會等同去呼叫plusone,並且帶入5。 如果上面的code你看懂了,恭喜你,block幾乎沒差太多,甚至更好用。 int ooxx() { typedef int (^myblock_type)(int a); myblock_type myblock = ^int(int a){ return a+1; }; NSLog(@"return value = %d", myblock(5)); } 第一行跟function pointer很像,原本function pointer是用*,但是block是用^ 第二行也是定義一個myblock_type的變數,但是不一樣的是他指到的是一個block, block的實作也是^開頭,緊接著是回傳type, 接著是傳入的參數,後面接上{....}就是block實作的內容啦。 就像前面所說的,block本身就是anonymous function的方式去定義, 有些時候anonymous function非常好用,例如定義event handler, 我們可以註冊event handler時,直接在後面寫event handler的邏輯, 可以很快的可以看到事件發生之後會要做哪些動作 不必像用function pointer或是delegate的方式需要把邏輯寫在另外一個function, 第三行(應該說第三個statement)就跟function pointer很像了,就是呼叫這個block。 當然block強大的地方就是block所包含的code可以使用外面的變數 例如下面的例子,temp是block外面所定義的, 但是我們可以直接拿來用 int ooxx() { int temp = 5; typedef int (^myblock_type)(int a); myblock_type myblock = ^int(int a){ return temp+1; }; NSLog(@"return value = %d", myblock(5)); } 但是值得注意的是,上面的code,在block中會把temp當作常數看待, 也就是說當定義block的時候,temp的值是5 即便我們在後來把temp改成1 但是得到的結果不會變, 這同樣也說明了一個特性, 那就是我們不能在block中,直接的修改(write)一個外部(區域)變數的值, 所以看下段code int ooxx() { int temp = 5; typedef int (^myblock_type)(int a); myblock_type myblock = ^int(int a){ temp++; return temp; }; NSLog(@"return value = %d", myblock(5)); } 這邊compile就會錯了,因為這邊不只是會讀temp的值,還會寫temp的值。 但是如果我們加上__block這個變數修飾.. 那此變數就可以在block中修改。 下面就是個例子 int ooxx() { __block int temp = 5; typedef int (^myblock_type)(int a); myblock_type myblock = ^int(int a){ temp++; return temp; }; NSLog(@"return value = %d", myblock(5)); } 以上就是常用的block的方法 事實上更常用的用法會是把block當參數傳到function, 這邊舉個GCD的例子, dispatch_async(queue, ^(void) { // some task here }); 這邊就是把一個block丟到queue中等待被背景執行, 而就是丟進一個傳入是void傳回是void的block 由於傳回是void,所以可以把^void(void){...}簡化成^(void){...} 在block的使用上常常會見到這種把block當參數的寫法,之後相信會漸漸的習慣。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 114.32.239.120 ※ 編輯: popcorny 來自: 114.32.239.120 (08/30 23:07)

08/31 09:24, , 1F
謝謝。...最近研究multi-thread,也常用到block
08/31 09:24, 1F
文章代碼(AID): #1ENFnGfQ (MacDev)
文章代碼(AID): #1ENFnGfQ (MacDev)