OC文档翻译8——使用块(Blocks)

8 Working with Blocks

An Objective-C class defines an object that combines data with related behavior. Sometimes, it makes sense just to represent a single task or unit of behavior, rather than a collection of methods.

OC中的类对象是数据和行为的封装。但有时,仅仅需要表示单一任务或单一行为单元,而不是一个方法的集合。

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.

块(Blocks)是OC中的语言级功能,使用它可以创建能够传入方法或函数的特殊代码单元,在这种情况下,代码单元就和普通的数值一样。实际上,块在OC中也是一种对象,可以将它们存入NSArray或NSDictionary等Collection中。块也可以在闭区域中获取数值,就和其它编程语言中的闭包和lambda表达式类似。

This chapter explains the syntax to declare and refer to blocks, and shows how to use blocks to simplify common tasks such as collection enumeration. For further information, see Blocks Programming Topics.

下面来介绍块的声明语法以及如何使用块来简化常规任务,比如集合的枚举等。更多内容详见:Blocks Programming Topics。

8.1 Block Syntax

The syntax to define a block literal uses the caret symbol (^), like this:

定义块的语法如下所示,使用上尖角符号^:

^{
    NSLog(@"This is a block");
}

As with function and method definitions, the braces indicate the start and end of the block. In this example, the block doesn’t return any value, and doesn’t take any arguments.

In the same way that you can use a function pointer to refer to a C function, you can declare a variable to keep track of a block, like this:

和函数或方法的定义类似,块的定义中,大括号用于指示块的开始和结束。上面的代码中,块没有返回任何值,也没有任何参数。

在C中,可以使用指针指向一个函数,同样,在OC中,可以声明指向块的变量(暂译:块变量),如下所示:

void (^simpleBlock)(void);

If you’re not used to dealing with C function pointers, the syntax may seem a little unusual. This example declares a variable called simpleBlock to refer to a block that takes no arguments and doesn’t return a value, which means the variable can be assigned the block literal shown above, like this:

如果你对C函数指针不熟悉,那看上面的代码就会感觉点奇葩了。代码中声明了一个simpleBlock的变量,它可以指向一个没有返回值,也没有参数的块,因此,这个变量可以指向之前定义的那个块,如下所示:

    simpleBlock = ^{
        NSLog(@"This is a block");
    };

This is just like any other variable assignment, so the statement must be terminated by a semi-colon after the closing brace. You can also combine the variable declaration and assignment:

和所有的变量赋值类似,上面也是一条赋值语句,所以要在末尾加上分号“;”。也可以直接将变量声明和块定义以及赋值语句合在一起写:

    void (^simpleBlock)(void) = ^{
        NSLog(@"This is a block");
    };

Once you’ve declared and assigned a block variable, you can use it to invoke the block:

一旦声明和赋值了一个块变量,就可以使用它来调用这个块:

    simpleBlock();

Note: If you attempt to invoke a block using an unassigned variable (a nil block variable), your app will crash.

注意:如果尝试使用一个未赋值的块变量(nil)来调用块,程序会崩溃。

8.1.1 Blocks Take Arguments and Return Values

Blocks can also take arguments and return values just like methods and functions.

As an example, consider a variable to refer to a block that returns the result of multiplying two values:

块也可以像方法或函数一样有参数和返回值。

例如,有一个指向块的变量,这个块的功能是返回两数的乘积:

    double (^multiplyTwoValues)(double, double);

The corresponding block literal might look like this:

相应的块内容如下:

    ^ (double firstValue, double secondValue) {
        return firstValue * secondValue;
    }

The firstValue and secondValue are used to refer to the values supplied when the block is invoked, just like any function definition. In this example, the return type is inferred from the return statement inside the block.

If you prefer, you can make the return type explicit by specifying it between the caret and the argument list:

firstValue和secondValue是块的参数,调用块时用于指定两个数的值,和函数定义类似。块的返回类型隐含在返回语句中。

你也可以显式地指定块的返回类型,如下所示:

     ^ double (double firstValue, double secondValue) {
        return firstValue * secondValue;
    }

Once you’ve declared and defined the block, you can invoke it just like you would a function:

完成了块的声明和定义之后,你就可以像调用函数一样调用块:

//定义一个指向块的指针:multiplyTwoValues
    double (^multiplyTwoValues)(double, double) =
                              ^(double firstValue, double secondValue) {
                                  return firstValue * secondValue;
                              };

    double result = multiplyTwoValues(2,4);

    NSLog(@"The result is %f", result);
8.1.2 Blocks Can Capture Values from the Enclosing Scope

As well as containing executable code, a block also has the ability to capture state from its enclosing scope.

If you declare a block literal from within a method, for example, it’s possible to capture any of the values accessible within the scope of that method, like this:

除了包含可执行代码外,块也能捕捉它所处的域内的变量状态。

例如在一个方法中声明块字面量,可以捕捉这个方法内的任何可访问数据:

- (void)testMethod {
    int anInteger = 42;

    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };

    testBlock();
}

In this example, anInteger is declared outside of the block, but the value is captured when the block is defined.

Only the value is captured, unless you specify otherwise. This means that if you change the external value of the variable between the time you define the block and the time it’s invoked, like this:

上例中,anInteger是在块外声明的,但是它的值能够被同一个方法内的块捕捉。

除非你特别指定,否则捕捉到的只是变量运行至块调用时的值。也就是说,如果运行至块定义和块调用之间时,在外部改变这个变量的值,如下所示:

    int anInteger = 42;

    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };

    anInteger = 84;

    testBlock();

the value captured by the block is unaffected. This means that the log output would still show:

块捕捉到的值不会被改变。也就是说经过上面的语句后,输出如下:

Integer is: 42

It also means that the block cannot change the value of the original variable, or even the captured value (it’s captured as a const variable).

这也意味着,块不能改变原来变量的值,也不能改变被捕捉到的数值(实际上捕捉到的是一个常量const数值)。

8.1.2.1 Use __block Variables to Share Storage

If you need to be able to change the value of a captured variable from within a block, you can use the __block storage type modifier on the original variable declaration. This means that the variable lives in storage that is shared between the lexical scope of the original variable and any blocks declared within that scope.

As an example, you might rewrite the previous example like this:

如果你想让块中被捕捉的变量值能够改变,可以在原始变量声明时,前面使用__block修饰符指定存储类别。这样的话,变量的存储空间是在自己的作用域和任何的块定义作用域之间被共享。
例如,你可以像下面这样重写之前的例子:

__block int anInteger = 42;

    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };

    anInteger = 84;

    testBlock();

Because anInteger is declared as a __block variable, its storage is shared with the block declaration. This means that the log output would now show:

因为anInteger被声明为__block变量,它的存储在块的声明中也能访问。意味着输出如下:

    Integer is: 84

It also means that the block can modify the original value, like this:

这也意味着能够在块中改变变量的值:

    __block int anInteger = 42;

    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
        anInteger = 100;
    };

    testBlock();
    NSLog(@"Value of original variable is now: %i", anInteger);

This time, the output would show:

此时,输出如下:

Integer is: 42
Value of original variable is now: 100
8.1.3 You Can Pass Blocks as Arguments to Methods or Functions

Each of the previous examples in this chapter invokes the block immediately after it’s defined. In practice, it’s common to pass blocks to functions or methods for invocation elsewhere. You might use Grand Central Dispatch to invoke a block in the background, for example, or define a block to represent a task to be invoked repeatedly, such as when enumerating a collection. Concurrency and enumeration are covered later in this chapter.

Blocks are also used for callbacks, defining the code to be executed when a task completes. As an example, your app might need to respond to a user action by creating an object that performs a complicated task, such as requesting information from a web service. Because the task might take a long time, you should display some kind of progress indicator while the task is occurring, then hide that indicator once the task is complete.

It would be possible to accomplish this using delegation: You’d need to create a suitable delegate protocol, implement the required method, set your object as the delegate of the task, then wait for it to call a delegate method on your object once the task finished.

Blocks make this much easier, however, because you can define the callback behavior at the time you initiate the task, like this:

之前的例子中都是直接在定义块之后就进行调用。而实际使用时,通常都是将块传入函数或方法中,在其他地方调用。例如,可以使用GCD(Grand Central Dispatch)在后台调用块,也可以定义一个块,并重复调用完成特定工作,如枚举集合。并发和枚举将在之后讨论。

块也被用于回叫,即定义一段完成某个任务之后才开始执行的代码。你的应用中可能会对用户的行为进行响应,例如,创建一个对象完成复杂任务,如向网络服务请求信息等。任务可能会运行很长的时间,这时你就可以让程序显示出任务的运行进度,而当任务完成时自动隐藏进度信息。

上面这样的过程可以使用委托(delegation)来完成:你需要建立一个合适的委托协议(protocol),实现必要(required)方法,为该任务设置委托对象,然后坐等任务结束时调用委托对象的方法。

然而,使用块可以简化这个过程,因为你可以在初始化某个任务时便定义回叫行为,如下所示:

- (IBAction)fetchRemoteInformation:(id)sender {
    [self showProgressIndicator];

    XYZWebTask *task = ...

    [task beginTaskWithCallbackBlock:^{
        [self hideProgressIndicator];
    }];
}

This example calls a method to display the progress indicator, then creates the task and tells it to start. The callback block specifies the code to be executed once the task completes; in this case, it simply calls a method to hide the progress indicator. Note that this callback block captures self in order to be able to call the hideProgressIndicator method when invoked. It’s important to take care when capturing self because it’s easy to create a strong reference cycle, as described later in Avoid Strong Reference Cycles when Capturing self.

In terms of code readability, the block makes it easy to see in one place exactly what will happen before and after the task completes, avoiding the need to trace through delegate methods to find out what’s going to happen.

The declaration for the beginTaskWithCallbackBlock: method shown in this example would look like this:

上面代码中调用了一个方法来显示任务进度条,然后创建并开始任务。回叫块(callback block)中包含任务结束时需要执行的代码,在这个例子中,它仅仅调用了一个方法来隐藏进度条。当被调用块中要使用hideProgressIndicator对象方法时,它需要捕捉self指针。但捕捉self时需要特别注意,因为通过捕捉self可以形成一个强引用环,详见Avoid Strong Reference Cycles。
就代码可读性而言,使用块能保证轻易看出代码中任务开始和结束时执行的是什么,而不用像使用委托时需要追踪委托对象方法的情况。
上面代码中beginTaskWithCallbackBlock方法声明如下所示:

//一个无返回值无参的callbackBlock作为参数:
- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock;

The (void (^)(void)) specifies that the parameter is a block that doesn’t take any arguments or return any values. The implementation of the method can invoke the block in the usual way:

代码中(void (^)(void))表明参数是一个无参无返回值的块。而这个方法的实现中可以像平常一样调用块:

- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock {
    ...
    callbackBlock();
}

Method parameters that expect a block with one or more arguments are specified in the same way as with a block variable:
定义一个使用有参块作为参数的方法如下所示:

- (void)doSomethingWithBlock:(void (^)(double, double))block {
    ...
    block(21.0, 2.0);
}
8.1.3.1 A Block Should Always Be the Last Argument to a Method

It’s best practice to use only one block argument to a method. If the method also needs other non-block arguments, the block should come last:

在实际工作中,方法参数内最好只含有一个块参数,而且如果方法中有其他的非块参数时,这个块参数最好写在最后,如下所示:

- (void)beginTaskWithName:(NSString *)name completion:(void(^)(void))callback;

This makes the method call easier to read when specifying the block inline, like this:

这样可以让方法调用更易阅读,特别是在将块定义直接写在参数内的时候,如下所示:

[self beginTaskWithName:@"MyTask" completion:^{
        NSLog(@"The task is complete");
}];
8.1.4 Use Type Definitions to Simplify Block Syntax

If you need to define more than one block with the same signature, you might like to define your own type for that signature.

As an example, you can define a type for a simple block with no arguments or return value, like this:

如果你想定义不止一个相同形式的块,你需要将它定义为自己的类型。

例如,你可以定义一个表示无参无返回值块的类型:

typedef void (^XYZSimpleBlock)(void);

You can then use your custom type for method parameters or when creating block variables:

接着就可以使用自定义的类型来作为方法参数或创建块变量:

XYZSimpleBlock anotherBlock = ^{
        ...
};
- (void)beginFetchWithCallbackBlock:(XYZSimpleBlock)callbackBlock {
    ...
    callbackBlock();
}

Custom type definitions are particularly useful when dealing with blocks that return blocks or take other blocks as arguments. Consider the following example:

自定义数据类型在使用块作为参数或返回块类型的块中尤其有用,比如下面的代码:

void (^(^complexBlock)(void (^)(void)))(void) = ^ (void (^aBlock)(void)) {
    ...
    return ^{
        ...
    };
};

The complexBlock variable refers to a block that takes another block as an argument (aBlock) and returns yet another block.

Rewriting the code to use a type definition makes this much more readable:

代码中complexBlock变量是一个块引用,它使用另外一个块作为参数(aBlock),并且返回一个块。

使用自定义数据类型可以大大提高可读性(比起上面的话的确“提(wu)高(ru)了可读性”),如下所示:

XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^ (XYZSimpleBlock aBlock) {
    ...
    return ^{
        ...
    };
};
8.1.5 Objects Use Properties to Keep Track of Blocks

The syntax to define a property to keep track of a block is similar to a block variable:

定义一个追踪块的属性和定义块变量类似:

@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end

Note: You should specify copy as the property attribute, because a block needs to be copied to keep track of its captured state outside of the original scope. This isn’t something you need to worry about when using Automatic Reference Counting, as it will happen automatically, but it’s best practice for the property attribute to show the resultant behavior. For more information, see Blocks Programming Topics.

注意:此时需要指定属性修饰符为copy,如果不是拷贝块的话,那么这个块在它原本作用域之外无法再追踪它所捕捉到的值。但在使用Automatic Reference Counting的时候并不需要担心这样的问题,因为会自动默认copy。不过在实际中,还是建议加上copy,以明示它的行为。详见Blocks Programming Topics。

A block property is set or invoked like any other block variable:

一个块属性的设置方法和调用方法和块变量类似:

self.blockProperty = ^{
        ...
};
self.blockProperty();

It’s also possible to use type definitions for block property declarations, like this:

也可以对块属性使用typedef声明,如下所示:

typedef void (^XYZSimpleBlock)(void);

@interface XYZObject : NSObject
@property (copy) XYZSimpleBlock blockProperty;
@end
8.1.6 Avoid Strong Reference Cycles when Capturing self

If you need to capture self in a block, such as when defining a callback block, it’s important to consider the memory management implications.

Blocks maintain strong references to any captured objects, including self, which means that it’s easy to end up with a strong reference cycle if, for example, an object maintains a copy property for a block that captures self:

比如在定义一个回叫块(callback Block)时,需要在块中捕捉self指针,那么很容易就会形成一个强引用循环。例如,一个对象维护了自身对一个块的拷贝属性,这个块又捕捉到了self,如下所示:

@interface XYZBlockKeeper : NSObject
@property (copy) void (^block)(void);
@end
@implementation XYZBlockKeeper
- (void)configureBlock {
    self.block = ^{
        [self doSomething];    // capturing a strong reference to self
                               // creates a strong reference cycle
    };
}
...
@end

The compiler will warn you for a simple example like this, but a more complex example might involve multiple strong references between objects to create the cycle, making it more difficult to diagnose.

To avoid this problem, it’s best practice to capture a weak reference to self, like this:

对于这个简单例子,编译器会发出警告。但有时如果是十分复杂的代码,而且代码中引入了许多的对象间强引用环,那么就可能很难让编译器发现并提出警告。

为了避免这样的问题,最好是在块捕捉self时使用弱引用(weak reference),如下所示:

- (void)configureBlock {
    XYZBlockKeeper * __weak weakSelf = self;
    self.block = ^{
        [weakSelf doSomething];   // capture the weak reference
                                  // to avoid the reference cycle
    }
}

By capturing the weak pointer to self, the block won’t maintain a strong relationship back to the XYZBlockKeeper object. If that object is deallocated before the block is called, the weakSelf pointer will simply be set to nil.

通过对self的弱引用,块不会维持对XYZBlockKeeper对象的强引用关系。如果在调用块之前该对象被释放,那么这个weakSelf会被自动设置成nil。

8.2 Blocks Can Simplify Enumeration

In addition to general completion handlers, many Cocoa and Cocoa Touch API use blocks to simplify common tasks, such as collection enumeration. The NSArray class, for example, offers three block-based methods, including:

更多地关于general completion handlers(通用完成管理?),许多Cocoa和Cocoa Touch API使用块来简化常规任务。比如集合的枚举,对于NSArray类来说,提供了三个基于块的方法,其中之一如下所示:

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;

This method takes a single argument, which is a block to be invoked once for each item in the array:

这个方法只有一个块参数,这个参数调用块来遍历数组中每一个元素:

    NSArray *array = ...
    [array enumerateObjectsUsingBlock:^ (id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"Object at index %lu is %@", idx, obj);
    }];

The block itself takes three arguments, the first two of which refer to the current object and its index in the array. The third argument is a pointer to a Boolean variable that you can use to stop the enumeration, like this:

而这个块参数自身又使用三个参数,前两个参数指定当前对象和它在数组中的下标。第三个参数是一个用于停止枚举的指针,如下所示:

    [array enumerateObjectsUsingBlock:^ (id obj, NSUInteger idx, BOOL *stop) {
        if (...) {
            *stop = YES;
        }
    }];

It’s also possible to customize the enumeration by using the enumerateObjectsWithOptions:usingBlock: method. Specifying the NSEnumerationReverse option, for example, will iterate through the collection in reverse order.

If the code in the enumeration block is processor-intensive—and safe for concurrent execution—you can use the NSEnumerationConcurrent option:

也可以使用enumerateObjectsWithOptions:usingBlock:方法来自定义遍历,比如指定其中的NSEnumerationReverse选项,效果就是反向遍历。
如果在枚举块中的代码是processor-intensive-and safe for concurrent execution(处理器优化及并发安全)的,你就可以使用NSEnumerationConcurrent选项:

    [array enumerateObjectsWithOptions:NSEnumerationConcurrent
                            usingBlock:^ (id obj, NSUInteger idx, BOOL *stop) {
        ...
    }];

This flag indicates that the enumeration block invocations may be distributed across multiple threads, offering a potential performance increase if the block code is particularly processor intensive. Note that the enumeration order is undefined when using this option.

The NSDictionary class also offers block-based methods, including:

这个选项的意思是枚举调用块过程可能会分布到多个线程中并行执行,如果块中的代码是针对处理器优化的,就可以有潜在的性能提升。但要注意,这个方法中枚举顺序并没有提前定义。

NSDictionary类也提供了一些基于块的方法,比如:

    NSDictionary *dictionary = ...
    [dictionary enumerateKeysAndObjectsUsingBlock:^ (id key, id obj, BOOL *stop) {
        NSLog(@"key: %@, value: %@", key, obj);
    }];

This makes it more convenient to enumerate each key-value pair than when using a traditional loop, for example.

当进行关键字-值匹配遍历时,使用这个方法会比传统的循环处理更方便。

8.3 Blocks Can Simplify Concurrent Tasks

A block represents a distinct unit of work, combining executable code with optional state captured from the surrounding scope. This makes it ideal for asynchronous invocation using one of the concurrency options available for OS X and iOS. Rather than having to figure out how to work with low-level mechanisms like threads, you can simply define your tasks using blocks and then let the system perform those tasks as processor resources become available.

一个块就是一个特殊的工作单元,在其内部融合了可执行代码及状态捕捉功能。这样就使得它在异步调用并且使用OSX和iOS中的某些并发选项时十分理想。你可以直接使用块来包含你的任务,然后让系统在处理器可用时自动完成这些任务,而不必去深究底层线程的工作原理。

OS X and iOS offer a variety of technologies for concurrency, including two task-scheduling mechanisms: Operation queues and Grand Central Dispatch. These mechanisms revolve around the idea of a queue of tasks waiting to be invoked. You add your blocks to a queue in the order you need them to be invoked, and the system dequeues them for invocation when processor time and resources become available.

OSX和iOS提供了一系列并发技术,包括两类任务调度措施:操作队列(Operation queues)和GCD(Grand Central Dispatch中央调度)。这些措施都是以任务队列模型为前提。将需要被调用的块插入任务队列,系统就会从队列中取出它们,并当处理器及系统资源可用时执行这些块。

A serial queue only allows one task to execute at a time—the next task in the queue won’t be dequeued and invoked until the previous task has finished. A concurrent queue invokes as many tasks as it can, without waiting for previous tasks to finish.

一个串行队列只能运行任务一个接一个地执行,后一个任务必须等待前一个任务执行完毕才开始执行。而一个并发队列中的任务则是只要资源足够就尽可能多地并发执行,而不用等待前一个任务执行完毕。

8.3.1 Use Block Operations with Operation Queues

An operation queue is the Cocoa and Cocoa Touch approach to task scheduling. You create an NSOperation instance to encapsulate a unit of work along with any necessary data, then add that operation to an NSOperationQueue for execution.

操作队列是Cocoa和Cocoa Touch中的一种任务调度方式。使用NSOperation对象来封装任务单元及相关必须数据,然后将它加入到NSOperationQueue中以便执行。

Although you can create your own custom NSOperation subclass to implement complex tasks, it’s also possible to use the NSBlockOperation to create an operation using a block, like this:

虽然你可以自己创建自己的NSOperation子类,以实现更加复杂的功能。但也可以直接使用NSBlockOperation类来创建一个任务块,如下所示:

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    ...
}];

It’s possible to execute an operation manually but operations are usually added either to an existing operation queue or a queue you create yourself, ready for execution:

也可以手动执行一个操作,但是这个操作通常都会被加入到已存在的操作队列或你自己创建的队列中去等待执行:

// schedule task on main queue:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperation:operation];

// schedule task on background queue:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];

If you use an operation queue, you can configure priorities or dependencies between operations, such as specifying that one operation should not be executed until a group of other operations has completed. You can also monitor changes to the state of your operations through key-value observing, which makes it easy to update a progress indicator, for example, when a task completes.

如果使用一个操作队列,你可以配置操作间的属性或依赖关系,比如指定一个操作必须在另外一组操作完成后才开始执行。你也可以通过key-value observing来监控你的任务状态的改变,比如当一个任务完成时,使用这中办法可以方便地更新任务进度条

For more information on operations and operation queues, see Operation Queues.

更多关于操作和操作队列的信息,参见Operation Queues。

8.3.2 Schedule Blocks on Dispatch Queues with Grand Central Dispatch

If you need to schedule an arbitrary block of code for execution, you can work directly with dispatch queues controlled by Grand Central Dispatch (GCD). Dispatch queues make it easy to perform tasks either synchronously or asynchronously with respect to the caller, and execute their tasks in a first-in, first-out order.

如果你需要调度执行任意代码块,可以直接和中央调度器(GCD)控制的调度队列进行交互。通过调度队列可以方便地控制任务同步及异步执行,对每个调用者的任务进行先进先出方式执行。

You can either create your own dispatch queue or use one of the queues provided automatically by GCD. If you need to schedule a task for concurrent execution, for example, you can get a reference to an existing queue by using the dispatch_get_global_queue() function and specifying a queue priority, like this:

也可以创建自己的调度队列或者使用一个GCD自动提供的调度队列。如果你需要让自己的任务并发执行,你可以使用dispatch_get_global_queue()函数获取一个已存在的队列,然后设置队列优先级,如下所示:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

To dispatch the block to the queue, you use either the dispatch_async() or dispatch_sync() functions. The dispatch_async() function returns immediately, without waiting for the block to be invoked:

需要将块插入队列时,使用dispatch_async()或dispatch_sync()函数。dispatch_async()函数直接返回,而不需要等待块被调用。

dispatch_async(queue, ^{
    NSLog(@"Block for asynchronous execution");
});

The dispatch_sync() function doesn’t return until the block has completed execution; you might use it in a situation where a concurrent block needs to wait for another task to complete on the main thread before continuing, for example.

dispatch_sync()函数直到块被执行完毕时才返回。你可能在并发块需要等待主线程上另外的任务完成的时候使用它。

For more information on dispatch queues and GCD, see Dispatch Queues.

更多信息,详见Dispatch Queues。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值