将va_list参数传递给可变参数函数会导致错误的访问错误

时间:2017-09-03 10:54:16

标签: objective-c swift3 singleton uialertview variadic-functions

我的项目中有一个单独的WarningManager类,如下所示

WarningManager.h文件

+ (WarningManager *)getInstance;

- (void) createAndPushWarning:(id<UIAlertViewDelegate>)actionDelegate isLocalisedStrings:(BOOL)localized text:(NSString *)text cancel:(NSString *)cancel buttons:(NSString*)firstObj, ...  NS_REQUIRES_NIL_TERMINATION;

WarningManager.m文件

static WarningManager *instance = nil;

+ (WarningManager *) getInstance {
    if (!instance) {
        instance = [[self alloc] init];
    }
    return instance;
}

    - (void) createAndPushWarning:(id<UIAlertViewDelegate>)actionDelegate isLocalisedStrings:(BOOL)localized text:(NSString *)text cancel:(NSString *)cancel buttons:(NSString*)firstObj, ... {
        va_list argumentList;
        va_start(argumentList, firstObj);

        NSMutableArray *buttonArray = [NSMutableArray array];

        if(firstObj){
            if(!localized){
                [buttonArray addObject:NSLocalizedString(firstObj, @"")];
            } else {
                [buttonArray addObject:firstObj];
            }
            id eachObject;
            while ((eachObject = va_arg(argumentList, id))){
                if(!localized){
                    [buttonArray addObject: NSLocalizedString(eachObject, @"")];
                }else{
                    [buttonArray addObject: eachObject];
                }
            }
        }

        if (!localized) {
            if(text){
                text = NSLocalizedString(text, @"");
            }
            if(cancel){
                cancel = NSLocalizedString(cancel, @"");
            }
        }

        va_end(argumentList);

        LogW(@"Warning : %@",text);

        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"My App Name", @"") message:text delegate:actionDelegate cancelButtonTitle:cancel otherButtonTitles:nil];
        for (NSString *button in buttonArray) {
            [alertView addButtonWithTitle:button];
        }
        [alertView show];
    }

当一个目标--C类文件调用如下

时,上面的代码很有效
[[WarningManager getInstance] createAndPushWarning:self isLocalisedStrings:NO text:@"Text want to display" cancel:nil buttons:@"Button 1", @"Button 2", nil];

现在我正在创建swift类,我想在其中使用相同的警告管理器,但是在swift中改变了可变参数函数语法,所以我在WarningManager类中通过替换variadic参数添加了另一个方法。

WarningManager.h文件

- (void) createAndPushWarning:(id<UIAlertViewDelegate>)actionDelegate isLocalisedStrings:(BOOL)localized text:(NSString *)text cancel:(NSString *)cancel agruments:(va_list)buttons

WarningManager.M文件

- (void) createAndPushWarning:(id<UIAlertViewDelegate>)actionDelegate isLocalisedStrings:(BOOL)localized text:(NSString *)text cancel:(NSString *)cancel agruments:(va_list)buttons {

    [self createAndPushWarning:actionDelegate isLocalisedStrings:localized text:text cancel:cancel buttons:(__bridge NSString *)(buttons), nil];
}

要从swift类中调用它,我在WarningManager + ArgumentList.swift文件中创建了一个对WarningManager类的扩展,如下所示

extension WarningManager {

    class func WarningWrapper(actionDelegate: UIAlertViewDelegate, isLocalizedString: Bool, text:String, cancel: String, _ args: CVarArg...) {

        withVaList(args) { _ in WarningManager.getInstance().createAndPushWarning(actionDelegate, isLocalisedStrings: isLocalizedString, text: text, cancel: cancel, agruments: getVaList(args)) }
    }
}

从swift类调用我的扩展方法

 WarningManager.WarningWrapper(actionDelegate: self, isLocalizedString: false, text: "Message to Display", cancel: "OK", "")

因此,当我从swift类调用此扩展时,我的访问错误

enter image description here

但我更改了我的WarningManager.m文件,它会显示警告。

- (void) createAndPushWarning:(id<UIAlertViewDelegate>)actionDelegate isLocalisedStrings:(BOOL)localized text:(NSString *)text cancel:(NSString *)cancel agruments:(va_list)buttons {

    [self createAndPushWarning:actionDelegate isLocalisedStrings:localized text:text cancel:cancel buttons:nil];
}

在某些地方,我在发送参数时犯了错误,我不知道如何解决这个问题。

感谢任何建议和想法,感谢提前阅读此问题的人。

1 个答案:

答案 0 :(得分:1)

您无法传递需要实际参数列表的va_list,即使它们是可变参数。将va_list视为数组。您无法传递需要参数列表的数组,并期望将每个数组项视为单个参数。整个数组作为单个第一个参数传递。

在一个理想的世界中,你会得到一个错误(在纯粹的Swift中),但是C的可变参数列表不包含足够的类型信息以便能够检测到这个错误。

您需要反转代码:将大部分代码放入va_list-taking版本(使用&#34;参数:&#34;参数),然后从可变版本调用该函数,该版本只调用va_start,调用va_list版本,然后调用va_end。