Преобразование содержимого NSArray в переменные (с ARC) для использования с NSString initWithFormat - PullRequest
13 голосов
/ 25 ноября 2011

Сегодня у нас есть некоторый код, который принимает NSArray и передает его в виде списка аргументов - [NSString initWithFormat: arguments], и мы пытаемся заставить его работать с ARC. Вот код, который использовали

NSString* format = @"Item %s and Item %s"; // Retrieved elsewhere
NSArray* args = [NSArray arrayWithObjects:@"1", @"2", nil]; // Retrieved elsewhere

// http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
char* argsList = (char*) malloc(sizeof(NSString*) * args.count);
[args getObjects:(id*) argsList];
NSString* message = [[[NSString alloc] initWithFormat:format arguments:argsList] autorelease];
free(argsList);

Любые рекомендации о том, как сделать этот ARC-совместимый? Или мы даже открыты для лучшего способа сделать это.

Ответы [ 5 ]

4 голосов
/ 29 января 2014

Это работает только для массивов с одним элементом

Ответ от chrisco работал хорошо, пока я не пошел на компиляцию с 64-битной архитектурой. Это вызвало ошибку:

EXC_BAD_ADDRESS тип EXC_I386_GPFLT

Решением было использование немного другого подхода для передачи списка аргументов методу:

+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments;
{
     __unsafe_unretained id  * argList = (__unsafe_unretained id  *) calloc(1UL, sizeof(id) * arguments.count);
    for (NSInteger i = 0; i < arguments.count; i++) {
        argList[i] = arguments[i];
    }

    NSString* result = [[NSString alloc] initWithFormat:format, *argList] ;//  arguments:(void *) argList];
    free (argList);
    return result;
}
1 голос
/ 09 февраля 2017

Расширяя ответ @ mcfedr, этот Swift 3 помощник выполняет свою работу:

import Foundation

@objc (FTStringFormat) public class StringFormat: NSObject {
    @objc public class func format(key: String, args: [AnyObject]) -> String {
        let locArgs: [CVarArg] = args.flatMap({ (arg: AnyObject) -> CVarArg? in
            if let arg = arg as? NSNumber {
                return arg.intValue
            }
            if let arg = arg as? CustomStringConvertible {
                return arg.description
            }
            return nil
        });
        return String(format: key, arguments: locArgs)
    }
}

Вызов из Objective-C:

[FTStringFormat formatWithKey:@"name: %@ age: %d" args:@[@"John", @(42)]]

Для спецификатора формата %@ мы используем протокол CustomStringConvertible Swift для вызова description на всех членах массива.

Поддержка всех спецификаторов числового формата, таких как %d и %f, на самом деле невозможна, поскольку объект NSNumber не показывает, является ли он целым числом или числом с плавающей точкой. Таким образом, мы могли поддержать только одного или другого. Здесь мы используем intValue, поэтому %d поддерживается, но %f и %g - нет.

0 голосов
/ 04 апреля 2016

Я пишу решение использовать NSInvocation и подписи.

Ответ создать в это . Также пишу подробное описание как это работает но только на русском ((

Может быть, это кому-нибудь поможет.

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

Не могу найти способ сделать это 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)
    }
}

Происходит какое-то волшебство, связанное с тем, что [CVarArgType] не ведет себя как обычный массив - но это работает в гибкой кросс-архитектуре так, как вы ожидаете.

0 голосов
/ 25 ноября 2011

Единственное, что вам нужно сделать, это удалить авто-релиз.

Вы используете malloc и освобождаете себя - ARC не заботится об этом.

...