IOS block編程指南 6 Block的使用

Using Blocks

Invoking a Block


If you declare a block as a variable, you can use it as you would a function, as shown in these two examples:

int (^oneFrom)(int) = ^(int anInt) {
    return anInt - 1;
};

printf("1 from 10 is %d", oneFrom(10));
// Prints "1 from 10 is 9"

float (^distanceTraveled)(float, float, float) =
                         ^(float startingSpeed, float acceleration, float time) {

    float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
    return distance;
};

float howFar = distanceTraveled(0.0, 9.8, 1.0);
// howFar = 4.9

Frequently, however, you pass a block as the argument to a function or a method. In these cases, you usually create a block “inline”.

調用block


如果你聲明一個block變量,你像使用一個函數一樣使用它,如下所示:

int (^oneFrom)(int) = ^(int anInt) {
    return anInt - 1;
};

printf("1 from 10 is %d", oneFrom(10));
// Prints "1 from 10 is 9"

float (^distanceTraveled)(float, float, float) =
                         ^(float startingSpeed, float acceleration, float time) {

    float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
    return distance;
};

float howFar = distanceTraveled(0.0, 9.8, 1.0);
// howFar = 4.9

通常情況,你將一個block作爲一個參數傳遞給一個函數(方法)。這時,你可以創建一個內聯block。

Using a Block as a Function Argument


You can pass a block as a function argument just as you would any other argument. In many cases, however, you don’t need to declare blocks; instead you simply implement them inline where they’re required as an argument. The following example uses the qsort_b function. qsort_b is similar to the standard qsort_r function, but takes a block as its final argument.

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };

qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    return strncmp(left, right, 1);
});
// Block implementation ends at "}"

// myCharacters is now { "Charles Condomine", "George", "TomJohn" }

Notice that the block is contained within the function’s argument list.

The next example shows how to use a block with the dispatch_apply function. dispatch_apply is declared as follows:

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));

The function submits a block to a dispatch queue for multiple invocations. It takes three arguments; the first specifies the number of iterations to perform; the second specifies a queue to which the block is submitted; and the third is the block itself, which in turn takes a single argument—the current index of the iteration.

You can use dispatch_apply trivially just to print out the iteration index, as shown:

#include <dispatch/dispatch.h>
size_t count = 10;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(count, queue, ^(size_t i) {
    printf("%u\n", i);
});

使用block作爲函數參數


你可以將一個block作爲參數傳給函數,就像你傳遞其他參數一個樣子。然而,在很多情況下,你不需要對block進行聲明;相反,它們就需要作爲一個參數,你只需簡單的inline實現他們。下面的例子使用了qsort_b函數。qsort_b類似於標準的qsort_r函數,但用block做爲最後的參數。

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };

qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    return strncmp(left, right, 1);
});
// Block implementation ends at "}"

// myCharacters is now { "Charles Condomine", "George", "TomJohn" }

注意,該block包含在函數的參數列表中

下面的例子展示了dispatch_apply函數如何使用block。dispatch_apply聲明如下:

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));

這個函數提交block給多次調用的調度隊列(這是啥意思?)。它需要三個參數,第一個指定的迭代次數來執行(迭代);二是指定block提交的隊列,第三個是block,而block本身又需要一個單一的參數,指當前的迭代次數。

你可以使用dispatch_apply打印出迭代次數(當前迭代數),如下:

#include <dispatch/dispatch.h>
size_t count = 10;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(count, queue, ^(size_t i) {
    printf("%u\n", i);
});

Using a Block as a Method Argument


Cocoa provides a number of methods that use blocks. You pass a block as a method argument just as you would any other argument.

The following example determines the indexes of any of the first five elements in an array that appear in a given filter set.

NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];

BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);

test = ^(id obj, NSUInteger idx, BOOL *stop) {

    if (idx < 5) {
        if ([filterSet containsObject: obj]) {
            return YES;
        }
    }
    return NO;
};

NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];

NSLog(@"indexes: %@", indexes);

/*
Output:
indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
*/

The following example determines whether an NSSet object contains a word specified by a local variable and sets the value of another local variable (found) to YES (and stops the search) if it does. Notice that found is also declared as a __block variable, and that the block is defined inline:

__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";

[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];

// At this point, found == YES

使用block作爲Method的參數


Cocoa提供了很多使用block的方法。你把一個block,向其他任何參數一樣,作爲一個方法的參數。下面的示例確定在給定篩選器集中出現的數組中的五個元素的第一個索引(這一段翻譯的很牽強)。

NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];

BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);

test = ^(id obj, NSUInteger idx, BOOL *stop) {

    if (idx < 5) {
        if ([filterSet containsObject: obj]) {
            return YES;
        }
    }
    return NO;
};

NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];

NSLog(@"indexes: %@", indexes);

/*
Output:
indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
*/

下面的示例確定NSSet對象包含一個字一個局部變量中指定的設置另一個本地變量的值(發現)是(停止搜索)如果它。本文由B9班的真高興發佈於CSDN博客注意發現也聲明爲__block變量,那塊定義內聯:

__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";

[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];

// At this point, found == YES

Copying Blocks


Typically, you shouldn’t need to copy (or retain) a block. You only need to make a copy when you expect the block to be used after destruction of the scope within which it was declared. Copying moves a block to the heap.

You can copy and release blocks using C functions:

Block_copy();
Block_release();

To avoid a memory leak, you must always balance a Block_copy() with Block_release().

Copying Blocks


通常情況下,你不需要copy(或retain)一個block。需要你copy一個block的時候是:你希望在block聲明的範圍被銷燬的時候,這個時候你copyblock進入heap(堆)。

你可以使用C方法copy或者release block。

Block_copy();
Block_release();

爲了避免內存泄露,你需要成對的寫 block_copy() 和block_release

Patterns to Avoid


A block literal (that is, ^{ … }) is the address of a stack-local data structure that represents the block. The scope of the stack-local data structure is therefore the enclosing compound statement, so you should avoid the patterns shown in the following examples:

void dontDoThis() {
    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {
        blockArray[i] = ^{ printf("hello, %d\n", i); };
        // WRONG: The block literal scope is the "for" loop.
    }
}

void dontDoThisEither() {
    void (^block)(void);

    int i = random():
    if (i > 1000) {
        block = ^{ printf("got i at: %d\n", i); };
        // WRONG: The block literal scope is the "then" clause.
    }
    // ...
}

避免的寫法


一個block字面值(即,{…})表示的是block的本地數據結構stack棧。因此,棧的局部數據結構的範圍是封閉的複合語句,因此,您應該避免在下面的示例中的寫法:

void dontDoThis() {
    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {
        blockArray[i] = ^{ printf("hello, %d\n", i); };
        // WRONG: The block literal scope is the "for" loop.
        // block字面值的作用域是,for循環
    }
}

void dontDoThisEither() {
    void (^block)(void);

    int i = random():
    if (i > 1000) {
        block = ^{ printf("got i at: %d\n", i); };
        // WRONG: The block literal scope is the "then" clause.
        // block字面值的作用域是if語句
    }
    // ...
}

Debugging


You can set breakpoints and single step into blocks. You can invoke a block from within a GDB session using invoke-block, as illustrated in this example:

$ invoke-block myBlock 10 20

If you want to pass in a C string, you must quote it. For example, to pass this string into the doSomethingWithString block, you would write the following:

$ invoke-block doSomethingWithString "\"this string\""

調試


你可以設置斷點和單步進入block。你可以調用一個在GDB會話使用的block,如下(?):

$ invoke-block myBlock 10 20

如果你想通過一個C字符串,你必須引用它。例如,把字符串傳入dosomethingwithstring block,你會寫:

$ invoke-block doSomethingWithString "\"this string\""
發佈了92 篇原創文章 · 獲贊 137 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章