パラメーターとしてのObjective-Cパスブロック


回答:


257

ブロックのタイプは、その引数と戻り値のタイプによって異なります。一般的な場合では、ブロックタイプは、関数ポインタ型である同じように宣言されているが、交換*^。ブロックをメソッドに渡す1つの方法は次のとおりです。

- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;

しかし、ご覧のとおり、これは厄介です。代わりにa typedefを使用してブロックタイプをよりクリーンにすることができます。

typedef void (^ IteratorBlock)(id, int);

次に、そのブロックを次のようなメソッドに渡します。

- (void)iterateWidgets:(IteratorBlock)iteratorBlock;

なぜidを引数として渡すのですか?たとえば、NSNumberを簡単に渡すことはできませんか?どうですか?
bas

7
あなたは確かのような強く型付けされた引数を渡すことができるNSNumber *か、std::string&または他の何かあなたが関数の引数として渡すことができます。これはほんの一例です。(で置き換えることidを除いて同等のブロックのNSNumber場合、typedefはになりますtypedef void (^ IteratorWithNumberBlock)(NSNumber *, int);。)
Jonathan Grynspan 2012

メソッド宣言を示します。ブロックに関する1つの問題は、「乱雑な」宣言スタイルでは、実際のブロック引数を使用した実際のメソッド呼び出しを明確かつ簡単に記述できないことです。
uchuugaka 2013

Typedefを使用すると、コードを記述しやすくなるだけでなく、ブロック/関数ポインターの構文が最もクリーンではないため、非常に読みやすくなります。
pyj 2014年

@ JonathanGrynspan、Swiftの世界から来ていますが、いくつかの古いObjective-Cコードに触れなければならない場合、ブロックがエスケープしているかどうかをどのように確認できますか?私は、デフォルトでは、ブロックが飾られている場合を除き、エスケープされていることを読んでNS_NOESCAPE、しかしenumerateObjectsUsingBlock、私は非エスケープされたと聞いてい、まだ私は見ていないNS_NOESCAPEサイト内のどこにでも、またアップルのドキュメント内のすべてに言及したエスケープされます。手伝ってくれますか?
マークA.ドノホー

62

この質問の最も簡単な説明は、次のテンプレートに従ってください。

1.メソッドパラメータとしてブロックする

テンプレート

- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
        // your code
}

-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
        // your code
}

ケースの他の使用法:

2.プロパティとしてブロック

テンプレート

@property (nonatomic, copy) returnType (^blockName)(parameters);

@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);

3.メソッドの引数としてブロックする

テンプレート

[anObject aMethodWithBlock: ^returnType (parameters) {
    // your code
}];

[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
    // your code
}];

4.ローカル変数としてブロックする

テンプレート

returnType (^blockName)(parameters) = ^returnType(parameters) {
    // your code
};

void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
    // your code
};

5. typedefとしてブロックする

テンプレート

typedef returnType (^typeName)(parameters);

typeName blockName = ^(parameters) {
    // your code
}

typedef void(^completionBlock)(NSArray *array, NSError *error);

completionBlock didComplete = ^(NSArray *array, NSError *error){
    // your code
};

1
[self saveWithCompletionBlock:^(NSArray * array、NSError * error){//コード}]; この例では、戻り値の型はvoidであるため無視されますか?
Alex

51

これは役に立つかもしれません:

- (void)someFunc:(void(^)(void))someBlock;

括弧がありません
newacct

これは私にとってはうまくいきましたが、前のものはうまくいきませんでした。ところで、メイト、ありがとう、それは確かに役に立ちました!
tanou 2014年

23

次のようにして、ブロックをブロックパラメーターとして渡します。

//creating a block named "completion" that will take no arguments and will return void
void(^completion)() = ^() {
    NSLog(@"bbb");
};

//creating a block namd "block" that will take a block as argument and will return void
void(^block)(void(^completion)()) = ^(void(^completion)()) {
    NSLog(@"aaa");
    completion();
};

//invoking block "block" with block "completion" as argument
block(completion);

8

以下の例のс関数を使用してブロックを渡すもう1つの方法。バックグラウンドとメインキューで何かを実行する関数を作成しました。

blocks.hファイル

void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));

blocks.mファイル

#import "blocks.h"

void performInBackground(void(^block)(void)) {
    if (nil == block) {
        return;
    }

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), block);
}

void performOnMainQueue(void(^block)(void)) {
    if (nil == block) {
        return;
    }

    dispatch_async(dispatch_get_main_queue(), block);
}

必要に応じて、blocks.hをインポートして起動します。

- (void)loadInBackground {

    performInBackground(^{

        NSLog(@"Loading something in background");
        //loading code

        performOnMainQueue(^{
            //completion hadler code on main queue
        });
    });
}

6

必要に応じて、ブロックを単純なプロパティとして設定することもできます。

@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);

ブロックプロパティが「コピー」であることを確認してください!

もちろん、typedefを使用することもできます。

typedef void (^SimpleBlock)(id);

@property (nonatomic, copy) SimpleBlock someActionHandler;



3

私は、振られた後にサイコロの値を返すクラス用のcompletementBlockを書きました:

  1. returndefでtypedefを定義します(.h上記の@interface宣言)

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
  2. @propertyブロックのを定義します(.h

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
  3. finishBlock.h)でメソッドを定義する

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
  4. 前の定義されたメソッドInsert .mファイルとコミットfinishBlockする@property前に定義されました

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
  5. completionBlock事前定義されたvariableType をトリガーに渡すには(completionBlock存在するかどうかを確認することを忘れないでください)

    if( self.completeDiceRolling ){
        self.completeDiceRolling(self.dieValue);
    }

2

このスレッドで与えられた答えにもかかわらず、私は本当に、ブロックを関数として、そしてパラメーターを指定して、関数を書くのに苦労しました。結局、これが私が思いついた解決策です。

loadJSONthreadJSON WebサービスのURLを取得し、このURLからJSONデータをバックグラウンドスレッドでロードし、結果のNSArray *を呼び出し元の関数に返す、汎用的な関数を記述したいと思っていました。

基本的に、私はバックグラウンドスレッドの複雑さをすべて、汎用の再利用可能な関数内に隠したいと思っていました。

この関数を呼び出す方法は次のとおりです。

NSString* WebServiceURL = @"http://www.inorthwind.com/Service1.svc/getAllCustomers";

[JSONHelper loadJSONthread:WebServiceURL onLoadedData:^(NSArray *results) {

    //  Finished loading the JSON data
    NSLog(@"Loaded %lu rows.", (unsigned long)results.count);

    //  Iterate through our array of Company records, and create/update the records in our SQLite database
    for (NSDictionary *oneCompany in results)
    {
        //  Do something with this Company record (eg store it in our SQLite database)
    }

} ];

...そしてこれは私が苦労したビットです:宣言する方法、およびデータがロードされたらブロック関数を呼び出して、BlockロードされたレコードのNSArray *を渡す方法:

+(void)loadJSONthread:(NSString*)urlString onLoadedData:(void (^)(NSArray*))onLoadedData
{
    __block NSArray* results = nil;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{

        // Call an external function to load the JSON data 
        NSDictionary * dictionary = [JSONHelper loadJSONDataFromURL:urlString];
        results = [dictionary objectForKey:@"Results"];

        dispatch_async(dispatch_get_main_queue(), ^{

            // This code gets run on the main thread when the JSON has loaded
            onLoadedData(results);

        });
    });
}

このStackOverflowの質問は、関数を呼び出してブロックをパラメーターとして渡す方法に関係するため、上記のコードを簡略化し、loadJSONDataFromURL関数を含めていません。

ただし、興味がある場合は、このJSONロード関数のコピーをこのブログで見つけることができます。http: //mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm

これが他の一部のXCode開発者に役立つことを願っています!(もしそうなら、この質問と私の答えに投票することを忘れないでください!)


1
これは私がiosとブロックで見た最高のトリックの1つです。大好きだ!!!!
portforwardpodcast

1

完全なテンプレートは次のようになります

- (void) main {
    //Call
    [self someMethodWithSuccessBlock:^{[self successMethod];}
                    withFailureBlock:^(NSError * error) {[self failureMethod:error];}];
}

//Definition
- (void) someMethodWithSuccessBlock:(void (^) (void))successBlock
                   withFailureBlock:(void (^) (NSError*))failureBlock {

    //Execute a block
    successBlock();

//    failureBlock([[NSError alloc]init]);

}

- (void) successMethod {

}

- (void) failureMethod:(NSError*) error {

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.