[POC]ブロックを使用した作業

Objective-Cのクラスは、関連する動作とデータを組み合わせたオブジェクトを定義します。時には、それだけで、単一のタスクまたはユニットの挙動のではなく、メソッドの集合を表現するために理にかなっています。

ブロックは、言語レベルの機能がC、あなたはそれらが値であるかのようにメソッドまたは関数に周り渡すことができるコードの別個のセグメントを作成することを可能にするのObjective-CとC ++、に添加されます。ブロックは、それらのようなコレクションに追加することができることを意味する、Objective-Cのオブジェクトです  NSArray か、  NSDictionary彼らはまた、他のプログラミング言語で閉鎖またはラムダとそれらが同様の作り、囲みスコープから値をキャプチャする機能を持っています。

この章では、宣言したブロックを参照するための構文を説明し、そのようなコレクションの列挙などの一般的なタスクを簡素化するためにブロックを使用する方法を示しています。詳細については、参照  ブロックプログラミングトピックを

ブロック構文

リテラルブロックを定義するための構文は、キャレット記号(使用する^このような)を、:

    ^ {
         NSLog(@ "これはブロックです");
    }

関数およびメソッドの定義と同様に、ブレースは、ブロックの開始と終了を示します。この例では、ブロックは、任意の値を返さない、と引数を取りません。

あなたがCの関数を参照する関数ポインタを使用することができます同様に、あなたはこのように、ブロックを追跡するために変数を宣言することができます。

    無効(^ simpleBlock)(無効)。

あなたがCの関数ポインタを扱うに慣れていない場合は、構文が少し変わったように見えることがあります。この例では、と呼ばれる変数を宣言  simpleBlock :変数を意味する引数を取らず、値を返さないブロックを参照するためには、このような上記に示したリテラルブロックを割り当てることができ

    simpleBlock = ^ {
        NSLog(@ "これはブロックです");
    }。

文が閉じ括弧の後にセミコロンで終了する必要がありますので、これは、単に他の変数の割り当てのようなものです。また、変数の宣言と代入を組み合わせることができます。

    ボイド(^ simpleBlock)(ボイド)= ^ {
        NSLog(@ "これはブロックです");
    }。

あなたが宣言され、ブロック変数が割り当てられたら、ブロックを呼び出すためにそれを使用することができます。

    simpleBlock();

注意:あなたが割り当てられていない変数(使用ブロック起動しようとすると  nil ブロック変数)を、あなたのアプリケーションがクラッシュします。

 

ブロックは、引数と戻り値を取ります

ブロックも引数を取るだけのメソッドや関数のように値を返すことができます。

一例として、2つの値を乗算した結果を返すブロックを参照するための変数を考慮してください。

    ダブル(^ multiplyTwoValues)(ダブル、ダブル);

対応するブロックリテラルは次のようになります。

    ^(ダブルfirstValue、ダブルsecondValue){
        firstValue * secondValueを返します。
    }

firstValue そして  secondValue ブロックが呼び出されたときだけ、任意の関数定義のように、供給された値を参照するために使用されます。この例では、戻り値の型は、ブロック内のreturn文から推測されます。

ご希望の場合は、キャレットと引数リストの間でそれを指定することで、明示的な戻り値の型を作ることができます。

    ^二重(二重firstValue、ダブルsecondValue){
        firstValue * secondValueを返します。
    }

あなたが宣言され、ブロックを定義したら、あなたはちょうどあなたが機能するようにそれを呼び出すことができます。

    ダブル(^ multiplyTwoValues)(ダブル、ダブル)=
                              ^(ダブルfirstValue、ダブルsecondValue){
                                  firstValue * secondValueを返します。
                              }。
 
    二重結果= multiplyTwoValues(2,4)。
 
    NSLog(@、結果 "結果は、%Fです")。

ブロックは、囲みスコープからの値をキャプチャすることができます

同様に、実行可能コードを含むように、ブロックは、その包囲範囲から状態を捕捉する能力を有します。

あなたがメソッド内からリテラルブロックを宣言した場合、例えば、それはこのように、その方法の範囲内でアクセス可能な値のいずれかをキャプチャすることが可能です。

- (ボイド)のtestMethod {
    int型anInteger = 42;
 
    ボイド(^ testBlock)(ボイド)= ^ {
        NSLog(@ "整数は次のとおりです。%I"、anInteger)。
    }。
 
    testBlock();
}

この例では、  anInteger ブロックの外で宣言されているが、ブロックが定義されているときに値が捕捉されます。

特に指定しない限り、唯一の値は、キャプチャされます。これはあなたの時間の間、変数の外部値を変更する場合は、このように、ブロックとそれが呼び出さだ時間を定義することを意味します。

    int型anInteger = 42;
 
    ボイド(^ testBlock)(ボイド)= ^ {
        NSLog(@ "整数は次のとおりです。%I"、anInteger)。
    }。
 
    anInteger = 84;
 
    testBlock();

ブロックによって捕捉された値は影響を受けません。これは、ログ出力は依然として示すだろうことを意味します:

整数です:42

また、ブロックは、元の変数、あるいはキャプチャ値(それはとして捕捉年代の値を変更できないことを意味する  const 変数)。

ストレージを共有する__block変数を使用します

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 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:

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

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:

- (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:

- (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)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);
}

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");
    }];

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:

XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^ (XYZSimpleBlock aBlock) {
    ...
    return ^{
        ...
    };
};

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.

 

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 void (^XYZSimpleBlock)(void);
 
@interface XYZObject : NSObject
@property (copy) XYZSimpleBlock blockProperty;
@end

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:

@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:

- (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.

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:

- (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:

    [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 *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.

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.

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.

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 queueinvokes as many tasks as it can, without waiting for previous tasks to finish.

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.

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:

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.

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

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.

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:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

キューにブロックをディスパッチするには、使用するか  dispatch_async() 、または  dispatch_sync() 機能。 dispatch_async() 関数が呼び出されるブロックを待たずに、すぐに返します。

dispatch_async(キュー、^ {
    NSLog(@ "非同期実行のためのブロック");
});

dispatch_sync() ブロックの実行が完了するまでの機能は戻りません。あなたは、同時ブロックが別のタスクには、例えば、継続する前にメインスレッド上で完了するのを待つ必要がある状況でそれを使用する場合があります。

ディスパッチキューとGCDの詳細については、参照  派遣キューを

おすすめ

転載: www.cnblogs.com/abugatti/p/poc_block.html