程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Objective-c中的Block(塊)詳解

Objective-c中的Block(塊)詳解

編輯:關於C語言

Block初探

在Objective-c中NSArray是很常用的容器之一,很多時候我們需要對數組中的數據進行排序,因此與下面類似的代碼會經常碰到:

NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {
 
    if ([obj1 integerValue] > [obj2 integerValue]) {
        return (NSComparisonResult)NSOrderedDescending;
    }
 
    if ([obj1 integerValue] < [obj2 integerValue]) {
        return (NSComparisonResult)NSOrderedAscending;
    }
    return (NSComparisonResult)NSOrderedSame;
}];
剛接觸objc的朋友可能會對這段代碼表示看不懂。
sortedArrayUsingComparator: ^(id obj1, id obj2) {
     // 代碼省略
}];
這個以^開頭的參數類型為NSComparetor,原型如下 :

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2); 

其實這個Comparetor的功能就類似於C語言中的函數指針,以及C++11、Java 8 、C#等語言中的Lambda表達式。

C語言中的函數指針

我們首先來回顧一下C語言中的最簡單的函數指針,在C語言中我們以

returntype (*name) (arg);
的格式定義一個函數指針變量。或者以 typedef的形式定義一個函數指針類型。示例代碼如下 :

// C語言的函數指針聲明
int (*addInC)(int , int ) ;

// 將此類型的函數指針聲明為一個AddOperation類型
typedef int (*AddOperation)(int, int);

// 普通C語言函數
int doAdd(int a, int b) {
    return a + b ;
}

//
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        // addInC指針變量指向doAdd函數
        addInC = doAdd;
        NSLog(@" addInC : %i.", addInC(5, 6)) ;	// 函數指針調用
        // 聲明一個aop函數指針變量,也指向doAdd
        AddOperation aop = doAdd ;
        NSLog(@" AddOperation : %i.", aop(100, 23) );
    }
    return 0;
}
輸出 :

addInC : 11.
AddOperation : 123.
首先是生命一個函數指針,addInC,然後在main函數中將addInC指向doAdd函數,這樣調用addInC(5, 6)就等於調用了doAdd(5, 6)。同理AddOperation也是這個原理。Java 8中的Lambda表達式

Objc中的塊

現在我們回到Objc中的Block, 就是上面NSArray中排序用到的塊。我們看看官方的定義 :

Blocks are a language-level feature added to C, Objective-C and C++, which allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary. They also have the ability to capture values from the enclosing scope, making them similar to closures or lambdas in other programming languages.
最後一句明確說明了塊類似於其他語言中的lambda以及閉包。

我們看看塊的聲明格式 :

returnType (^name) (arguments) 
其中returnType代表返回值類型,name代表變量名,argments代表參數列表,如果沒有參數可以寫成(void)或者()。

再看看塊的定義格式 :

^ [returnType] (arguments) {  statements };
其中returnType是可選的,arguments是參數列表,statements是實現功能的代碼。我們看如下示例 :

// 有返回值, 但是在定義塊函數時返回值可選.
int (^addUseBlock) (int, int) = ^/* int*/ (int a, int b ) { return a + b ; };
我們聲明了addUseBlock變量,然後讓其指向一個有兩個int參數的塊。在定義時返回類型int可以省略。調用代碼如下:

        // 使用Block函數
        NSLog(@" add use Blockm, Result ==> %i.", addUseBlock(3,4) );
最終會返回 3 + 4的結果。

將塊定義為一種類型

我們可以想C語言中的函數指針一樣,使用typedef將塊定義為一種類型,這樣便於將塊當做函數傳遞。下面我們定義一個Comparetor類型,有一個int返回值,有兩個int行參數,示例如下:

// 定義一個Compare類型
typedef int (^Comparetor) (int arg1, int arg2) ;
這個看起來就很像NSArray中的NSComparator了,只是返回的類型不同而已。
這個時候我們使用塊來當做參數就容易多了,如下示例 :

// Block當做參數
int helpCompare(Comparetor cmp ) {
    return cmp(3, 4);
}

// 返回一個Block對象
Comparetor getComparetor(){
    return ^(int a, int b) { return a > b ? -1 : 1; };
}

//
int main(int argc, const char * argv[])
{

    @autoreleasepool {

        // 使用Comparetor類型定義Block
        Comparetor cmp = ^(int a, int b) {
            if ( a == b ) {
                return 0 ;
            }
            return a > b ? 1 : -1 ;
        } ;
        
        NSLog(@"use Comparetor, Result ==> %i.", cmp(3,4) );
        // 使用Comparetor類型的cmp當做參數
        NSLog(@"use Comparetor, Result ==> %i.", helpCompare(cmp) );
        
        // 使用Comparetor類型的匿名函數當做參數, 該匿名block固定返回123
        NSLog(@"use Comparetor, Result ==> %i.", helpCompare( ^(int a, int b) {return 123; } ) );
        // 通過函數返回一個Block, 並且把它當做參數傳遞給helpCompare
        NSLog(@"use Comparetor, Result ==> %i.", helpCompare( getComparetor() ) );
        
    }
    return 0;
}
輸出如下 :

use Comparetor, Result ==> -1.
use Comparetor, Result ==> -1.
use Comparetor, Result ==> 123.
use Comparetor, Result ==> 1.

塊使用上下文變量

塊代碼也可以使用其所在范圍的變量,但是塊代碼中不能修改該變量的值或者引用, 例如塊所在函數的變量、類的成員變量。如果需要在塊代碼中修改變量值,可以使用__block定義變量。示例如下 :

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        // 使用上下文變量
        int valueInMain = 222 ;
        
        void (^printValue)() = ^() {
            NSLog(@"使用函數上下文的變量(不能修改) : %i.", valueInMain) ;
        } ;
        printValue();
        
        __block int anInteger = 42;
        void (^testBlock)(void) = ^{
            anInteger = 100;
            NSLog(@"使用__block聲明的變量(可以修改) : %i", anInteger);
        };
        testBlock();
        
    }
    return 0;
}

輸出如下 :

使用函數上下文的變量(不能修改) : 222.
使用__block聲明的變量(可以修改) : 100

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved