关于block多参数的情况

关于block的原理,唐巧已经在他的blog里面做了很详尽和专业的分析: http://blog.devtang.com/blog/2013/07/28/a-look-inside-blocks/
一言以蔽之:闭包是一个函数(或指向函数的指针),再加上该函数执行的外部的上下文变量(有时候也称作自由变量)。block是Objective-C语言对于闭包的实现。

我们大多数用block的时候,一般只会用到block带确定个参数的情况,比如:

void (^callback)(int);
void (^callback)(NSDate *);

如果现在要实现block的参数个数不定的情况,该怎么实现呢?记得我们用UIAlertView类的时候,有个初始化方法为:

- (id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ...

注意到没有,otherButtonTitles后面的3个省略号。对了,这就是C语言实现参数不定的方法。我们把它移植到block中来,比如:

void (^callback)(NSDate *, ...)

问题又来了,怎么获得NSDate后面的参数呢。C语言提供了var_start,var_end,var_arg 3个宏可以用来获取NSDate后面的参数。关于var_start,var_end,var_arg的详细信息,可以参考:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html
有一点需要注意,函数参数是以数据结构栈的形式存取,从右至左入栈。参数存放在内存的栈中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,

比如我们有一个类TestView,在点击这个类的时候,触发一个block:

TestView.h

typedef void(^CallbackBlock)(NSObject *object, ...);

@interface TestView : UIView

@property (nonatomic, copy) CallbackBlock callbackBlock;

@end

TestView.m

#import "TestView.h"

@implementation TestView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

    }

    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if(_callbackBlock) {
        _callbackBlock(@"1", @"2", [NSDate date], [NSArray arrayWithObjects:@"1", nil], nil);
    }
}
@end

注意,参数列表的末尾一定要以nil结束,否则会报野指针的异常。

ViewController.h

#import <UIKit/UIKit.h>

#import "TestView.h"

@interface ViewController : UIViewController

@end

ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];

    TestView *testView = [[TestView alloc] initWithFrame:CGRectMake(10, 100, 300, 100)];
    testView.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:testView];
    testView.callbackBlock = ^(NSObject *obj1, ...) {

        va_list ap;
        NSString *str_ = nil;

        va_start(ap, obj1);
        str_ = va_arg(ap, id);

        while(str_ != nil) {
            NSLog(@"str_:%@", str_);
            str_ = va_arg(ap, NSString *);
        }
        va_end(ap);
    };
}

以后在工程中,只用定义一个block就可以了:

typedef void(^CallbackBlock)(NSObject *object, ...);