Поддельный va_list в ARC
Мне нужно создать в приложении 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;
Я пробовал все, но до сих пор не могу избавиться от этой ошибки...
Есть ли решение для создания поддельного va_list
с включенным ARC? Что я делаю неправильно?
Ответы
Ответ 1
EDIT: Это больше не работает. Как было предусмотрено в первоначальном ответе, ABI, похоже, изменился из-за этого ответа
Немного поиграл и заставил его работать - Double проверил на утечку или оставленную память и не видел.
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, как это, чтобы получить семантику сохранения/выпуска на произвольном фрагменте памяти. Это не обязательно относится к проблеме, но это аккуратный маленький трюк.
В качестве примечания для будущих читателей: Faking up va_list, как это происходит, работает с текущим ABI для MacOS и iOS, но в целом он не переносимый, а не хороший подход.
Ответ 2
Это возможно, если вы захотите добавить немного своего быстрого проекта!
Важным битом является сопоставление 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)
}
}