Objective-C传递块作为参数


Answers:


256

块的类型根据其参数和返回类型而有所不同。在一般情况下,块类型中声明的相同方式函数指针类型,但在更换*^。将块传递给方法的一种方法如下:

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

但是,您可以看到,这很混乱。您可以改为使用typedef来使块类型更整洁:

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

然后将该块传递给类似这样的方法:

- (void)iterateWidgets:(IteratorBlock)iteratorBlock;

为什么要传递id作为参数?例如,是否可以轻松地传递NSNumber?看起来怎么样?
bas 2012年

7
您当然可以传递强类型参数,例如NSNumber *std::string&或其他任何可以作为函数参数传递的参数。这只是一个例子。(对于一个块这不同的是用当量idNSNumber时,typedef将是typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
乔纳森Grynspan

这显示了方法声明。块的麻烦之处在于,“ messy”声明样式无法使使用实际的块参数编写实际的方法调用变得清晰而轻松。
uchuugaka

Typedef不仅使代码更容易编写,而且由于块/函数指针的语法不是最干净的,因此也大大简化了代码的读取。
pyj 2014年

@JonathanGrynspan,来自Swift世界,但必须接触一些旧的Objective-C代码,如何判断某个块是否在转义?我读到默认情况下,除非用修饰NS_NOESCAPE,否则块都是转义的,但是enumerateObjectsUsingBlock我被告知是不转义的,但是我NS_NOESCAPE在网站上看不到任何地方,Apple文档中也没有提到转义。你能帮我吗?
Mark A. Donohoe

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){//您的代码}]; 在此示例中,返回类型被忽略,因为它是空的?
亚历克斯(Alex)

51

这可能会有所帮助:

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

您缺少括号
newacct 2012年

这一个对我有用,而上一个没有。顺便说一句,谢谢队友,那确实是有帮助的!
塔努

23

您可以这样做,将block作为block参数传递:

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

在下面的示例中,使用с函数传递块的另一种方法。我创建了一些函数,可以在后台和主队列中执行任何操作。

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

如果适用,您还可以将block设置为简单属性:

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

确保block属性是“ copy”!

当然,您也可以使用typedef:

typedef void (^SimpleBlock)(id);

@property (nonatomic, copy) SimpleBlock someActionHandler;



3

我为一个类编写了completeBlock,该类将在摇动后返回骰子的值:

  1. 使用returnType定义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. 插入先前定义的方法.m文件并提交finishBlock@property之前定义

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
  5. 触发completionBlock传递预定义的variableType到它(别忘了检查是否completionBlock存在)

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

2

尽管在该线程上给出了答案,但我确实很难编写一个将Block作为函数的带参数的函数。最终,这是我想出的解决方案。

我想编写一个通用函数,loadJSONthread它将使用JSON 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函数,并传递已加载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问题涉及如何调用函数,将Block作为参数传递,因此我简化了上面的代码,但没有包括 loadJSONDataFromURL函数。

但是,如果您有兴趣,可以在此博客上找到此JSON加载函数的副本:http : //mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm

希望这对其他XCode开发人员有所帮助!(如果可以,请不要忘记投票这个问题和我的答案!)


1
认真地讲,这是我见过的针对ios和块的最佳技巧之一。爱它的男人!
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 {

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.