关于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, ...);