将NSArray内容转换为varargs(使用ARC)以与NSString initWithFormat一起使用

时间:2011-11-25 19:17:23

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

我们今天有一些代码接受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兼容的任何建议?或者我们甚至愿意接受更好的方式。

6 个答案:

答案 0 :(得分:4)

这仅适用于具有单个元素的数组

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 :(得分:1)

扩展@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)]]

对于%@格式说明符,我们使用Swift的CustomStringConvertible协议,以便在所有数组成员上调用description

支持所有数字格式说明符,例如%d%f实际上是不可能的,因为NSNumber对象不会显示它是整数还是浮点数。所以我们只能支持其中一个。我们在这里使用intValue,因此支持%d,但不支持%f%g

答案 2 :(得分:0)

您唯一需要做的就是删除自动释放。

你是自己动手并且自由自在 - ARC并不关心这一点。

答案 3 :(得分:0)

找不到办法做这个obj-c但是一个快速的助手类终于搞定了(我的整个项目是obj-c除了这个类)

[CVarArgType]

有一些神奇的事情与Q.allSettled不像普通数组的行为有关 - 但这可以按照您期望的灵活的交叉架构方式工作。

答案 4 :(得分:0)

我使用NSInvocation和签名编写解决方案。

this中回答创建。 我也写detailed description它是如何工作的,但仅限于俄语((

也许对某人有帮助。

答案 5 :(得分:0)

我尝试了mcfedr的代码。不知何故,我的Xcode 11将CVarArgType视为未声明的类型,因此我对此进行了一段时间的研究。

我不理解他/她代码的关闭部分。而且,我只是简化为使用CVarArg运算符将每个元素强制转换为as!

func format(key: String, args: [Any]) -> String {
    return String(format: key, arguments: args.map { ($0 as! CVarArg) })
}

let doubleValue: Double = 1.25
let floatValue: Float = 2.75
let intValue: Int = 3
let numberValue: NSNumber = 4.5 as NSNumber
let hello: String = "Hello"
let world: NSString = "World" as NSString
print(format(key: "double: %f, float: %f, int: %d, number: %@, %@, %@", args: [doubleValue, floatValue, intValue, numberValue, hello, world]))

// double: 1.250000, float: 2.750000, int: 3, number: 4.5, Hello, World

在swift 5.1下似乎工作正常,但可能会有一些陷阱。