ARC中的虚假va_list

时间:2011-11-21 12:44:39

标签: objective-c ios automatic-ref-counting variadic-functions

我需要在iOS应用程序中创建一个假的va_list来传递给NSString initWithFormat:arguments:函数,这是我的代码:

NSArray *fixedArguments = [[NSArray alloc] initWithArray:arguments]; 

NSRange range = NSMakeRange(0, [fixedArguments count]);

va_list fakeArgList = (va_list)malloc(sizeof(NSString *) * [fixedArguments count]);

__unsafe_unretained id *ptr = (__unsafe_unretained id *)fakeArgList;

[fixedArguments getObjects:ptr range:range];

content = [[NSString alloc] initWithFormat:outputFormat
                                            arguments:(va_list)fakeArgList];
free(fakeArgList);

编译器在投射线上抱怨此消息:

error: cast of a non-Objective-C pointer type 'va_list' (aka 'char *') to '__unsafe_unretained id *' is disallowed with ARC

getObjects:range:函数定义如下:

- (void)getObjects:(id __unsafe_unretained [])objects range:(NSRange)range;

我已经尝试了一切,但仍然无法摆脱这个错误......

是否有创建启用ARC的假va_list的解决方案?我做错了什么?

2 个答案:

答案 0 :(得分:30)

编辑:这不再适用。正如在最初的答案中所预见的那样,ABI似乎已经从这个答案中改变了

玩了一会儿并让它发挥作用 - 双重检查泄漏或遗弃的记忆并没有看到任何。

    NSArray *fixedArguments = [[NSArray alloc] initWithObjects: @"foo", @"bar", @"baz", nil]; 

    NSRange range = NSMakeRange(0, [fixedArguments count]);

    NSMutableData* data = [NSMutableData dataWithLength: sizeof(id) * [fixedArguments count]];    

    [fixedArguments getObjects: (__unsafe_unretained id *)data.mutableBytes range:range];

    NSString* content = [[NSString alloc] initWithFormat: @"1: %@ 2: %@ 3: %@"  arguments: data.mutableBytes];

    NSLog(@"%@", content);

我喜欢(ab)使用这样的NSMutableData来获取任意内存块上的保留/释放语义 - 它不一定与手头的问题相关,但它是一个巧妙的小技巧。

作为对未来读者的一个注意事项:假装这样的va_list恰好适用于当前的Mac OS和iOS版ABI,但总的来说它不是可移植的,也不是一个好的方法。

答案 1 :(得分:0)

如果你愿意为你的项目添加一些swift,那么它是可能的!

重要的一点是NSArray到[CVarArgType]的映射,它是va_list的快速等价物。如果您尝试将[AnyObject]转换为[CVarArgType],则会导致运行时崩溃,但使用map我们可以明确地生成所需的列表。

代码的其余部分是我制作的包装器,以便我可以从obj-c调用它。您可以为要以这种方式调用的任何obj-c函数创建一个包装器。

@objc class StringFormat: NSObject {
    class func format(key: String, args: [AnyObject]) -> String {
        let locArgs: [CVarArgType] = args.map({ (arg: AnyObject) -> CVarArgType in
            if let iArg = (arg is NSNumber ? arg.intValue : nil) {
                return iArg
            }
            return arg as! CVarArgType
        });
        return String(format: key, arguments: locArgs)
    }
}