有时候,我们想定义一个参数个数可变的方法。例如我们想用系统的alertView去实现一些功能,但是系统的提供的不能完全的满足我们的需求,当然我们可以通过category来实现,给UIAlertView扩展一些方法但是,但是如果要添加某些属性,通过category实现起来就会麻烦点了(runtime 可以实现)。
看了下系统的alert View 的init方法中使用了一个 NS_REQUIRES_NIL_TERMINATION 的宏
- (instancetype)initWithTitle:(nullable NSString )title message:(nullable NSString *)message delegate:(nullable id /<UIAlertViewDelegate>*/)delegate cancelButtonTitle:(nullable NSString *)cancelButtonTitle otherButtonTitles:(nullable NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION NS_EXTENSION_UNAVAILABLE_IOS("Use UIAlertController instead.");
if !defined(NS_REQUIRES_NIL_TERMINATION)
#if TARGET_OS_WIN32
#define NS_REQUIRES_NIL_TERMINATION
#else
#if defined(__APPLE_CC__) && (__APPLE_CC__ >= 5549)
#define NS_REQUIRES_NIL_TERMINATION __attribute__((sentinel(0,1)))
#else
#define NS_REQUIRES_NIL_TERMINATION __attribute__((sentinel))
#endif
#endif
endif
attribute((sentinel)) 告知编译器 需要一个结尾的参数,告知编译器参数的列表已经到最后一个不要再继续执行下去了
按照alert的init放,写了个事例demo
typedef void(^KSEditAlertBlock)(NSInteger buttonIndex, id returnObj);
@interface KSAlertView : NSObject
- (instancetype)initAlertTitle:(NSString *)title message:(NSString *)alertMessage cancelButtonTitle:(NSString *)cancleButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
- (void)setButtonPressBlock:(KSEditAlertBlock)block;
这样,我们在传参数的时候,就可以将多个(不定)参数传递到方法里面
参数传进去了,并不是意味着我们可以直接使用了,需要使用参数,我们还需要做其他的事情
NSMutableArray *listArray = [NSMutableArray array];
va_list argList;
if (otherButtonTitles) {
// va_start(ap, param) __builtin_va_start(ap, param)
// va_start宏,获取可变参数列表的第一个参数的地址,在这里是获取otherButtonTitles的内存地址,这时argList的指针 指向otherButtonTitles
va_start(argList, otherButtonTitles);
id temp; // 临时指针变量
// va_arg(ap, type) __builtin_va_arg(ap, type)
// var_arg 宏 返回当前指定类型的值,并且把 argList 的位置指向变参表的下一个变量位置
while ((temp = va_arg(argList, id))) {
[listArray addObject:temp];
}
va_end(argList); // 关闭 argList指针
}
self.alert = [[UIAlertView alloc] initWithTitle:title message:alertMessage delegate:self cancelButtonTitle:cancleButtonTitle otherButtonTitles:otherButtonTitles, nil];
for (NSString *buttonName in listArray) {
[self.alert addButtonWithTitle:buttonName];
}
self.alert.alertViewStyle = UIAlertViewStylePlainTextInput;
_textField = [self.alert textFieldAtIndex:0];
_textField.placeholder = @"请输入";
_textField.keyboardType = UIKeyboardTypeNumberPad;
[self.alert show];
这时候我们就可以将传到方法内的可变参数表中的参数正确的使用了 当然在调用的时候一定要记住,在可变参数的结尾,要加上nil 这个哨兵参数,这样取参循环才会结束,否则temp = va_arg(argList, id)这个就会一直取值,当取到未分配的内存地址,listArray赋值的时候就会造成crash。