фальшивый va_list в ARC - PullRequest
       30

фальшивый va_list в ARC

16 голосов
/ 21 ноября 2011

Мне нужно создать в приложении 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? Что я делаю не так?

Ответы [ 2 ]

30 голосов
/ 21 ноября 2011

РЕДАКТИРОВАТЬ: Это больше не работает. Как и предполагалось в первоначальном ответе, 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 может работать с текущим ABI для MacOS и iOS, но в целом он не переносимый и не очень хороший подход.

0 голосов
/ 25 сентября 2015

Это возможно, если вы хотите добавить немного 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)
    }
}
...