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

问题描述 投票:13回答:6

[我们今天有一些代码,它使用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兼容的任何建议?或者,我们甚至愿意采取更好的方法。

objective-c automatic-ref-counting variadic-functions
6个回答
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
投票

无法找到执行此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]的行为不像普通数组,这有一些魔术,但这可以按照您期望的那样灵活的跨体系结构方式工作。


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


0
投票

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

您正在分配和释放自己-ARC对此并不关心。


0
投票

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

this中创建答案。我也写了detailed description它是如何工作的,但仅在俄语((]

也许对某人有帮助。


0
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.