是否有任何方法来创建一个新的 NSString从一种格式串像@"xxx=%@,yyy=%@",一个是的对象?

在NSSTring类的方法有很多,如:

- (id)initWithFormat:(NSString *)format arguments:(va_list)argList
- (id)initWithFormat:(NSString *)format locale:(id)locale arguments:(va_list)argList
+ (id)stringWithFormat:(NSString *)format, ...

但不是他们需要一个是作为参数,和我无法找到一种方式来创建一个va_list从一个是...

有帮助吗?

解决方案

它实际上是不努力创造一个va_list从一个是.见马特*加拉格尔的 优秀的文章 该问题。

这里是一个NSString类别做你想要什么:

@interface NSString (NSArrayFormatExtension)

+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments;

@end

@implementation NSString (NSArrayFormatExtension)

+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments
{
    char *argList = (char *)malloc(sizeof(NSString *) * arguments.count);
    [arguments getObjects:(id *)argList];
    NSString* result = [[[NSString alloc] initWithFormat:format arguments:argList] autorelease];
    free(argList);
    return result;
}

@end

然后:

NSString* s = [NSString stringWithFormat:@"xxx=%@, yyy=%@" array:@[@"XXX", @"YYY"]];
NSLog( @"%@", s );

不幸的是,64位,动作列表格式已经改变,因此上述代码不再工作。并且大概不应被用无论如何给它取决于格式,显然是受到改变。鉴于没有真正可靠的方法,以创建一个va_list,一个更好的解决方案是简单的数量限制的辩论,以一个合理的最大(说10)和然后打电话接与第一次10个参数,事情是这样的:

+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments
{
    if ( arguments.count > 10 ) {
        @throw [NSException exceptionWithName:NSRangeException reason:@"Maximum of 10 arguments allowed" userInfo:@{@"collection": arguments}];
    }
    NSArray* a = [arguments arrayByAddingObjectsFromArray:@[@"X",@"X",@"X",@"X",@"X",@"X",@"X",@"X",@"X",@"X"]];
    return [NSString stringWithFormat:format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9] ];
}

其他提示

在此基础上回答使用自动参考计数(ARC): https://stackoverflow.com/a/8217755/881197

添加一个类别, NSString 用以下方法:

+ (id)stringWithFormat:(NSString *)format array:(NSArray *)arguments
{
    NSRange range = NSMakeRange(0, [arguments count]);
    NSMutableData *data = [NSMutableData dataWithLength:sizeof(id) * [arguments count]];
    [arguments getObjects:(__unsafe_unretained id *)data.mutableBytes range:range];
    NSString *result = [[NSString alloc] initWithFormat:format arguments:data.mutableBytes];
    return result;
}

一个解决方案,来到我的头脑我就可以创建一个方法工作的一个固定的大量参数,如:

+ (NSString *) stringWithFormat: (NSString *) format arguments: (NSArray *) arguments {
    return [NSString stringWithFormat: format ,
          (arguments.count>0) ? [arguments objectAtIndex: 0]: nil,
          (arguments.count>1) ? [arguments objectAtIndex: 1]: nil,
          (arguments.count>2) ? [arguments objectAtIndex: 2]: nil,
          ...
          (arguments.count>20) ? [arguments objectAtIndex: 20]: nil];
}

我还可以添加一检查,看如果格式串已超过21'%'字并把一个异常,在这种情况。

@Chuck 是正确的有关事实的你 不能换一个是成varargs.然而,我不建议在寻找模式 %@ 字符串中和替换,它每次.(替换人物中间的串通常是相当低效率的,并不是一个好主意如果你可以完成同样的事情在不同的方式。) 这里是一个更有效的方式来创建一个字符格式,你说的是:

NSArray *array = ...
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array) {
    [newArray addObject:[NSString stringWithFormat:@"x=%@", [object description]]];
}
NSString *composedString = [[newArray componentsJoinedByString:@", "] retain];
[pool drain];

包括我自动释放游泳池好的客房服务,因为一个串将创建对各列项,而可变阵列是个如此。你可以容易地做到这一方法/多功能和返回 composedString 没有保留和处理的自动释放其他地方代码,如果所希望的。

这答案是越野车。正如所指出的,没有解决这个问题,是保证工作时,新的平台推出比其他使用"10元件阵列"的方法。


答案通过solidsun运作良好,直到我去汇编有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;
}

这只是工作组与一个单一的元素

还有 是没有一般的方式 通过一系列函数或方法的使用varargs.在这种特定情况下,然而,可以假的使用是这样的:

for (NSString *currentReplacement in array)
    [string stringByReplacingCharactersInRange:[string rangeOfString:@"%@"] 
            withString:currentReplacement];

编辑:所接受的答案要求有一种方法来这样做,但无论多么脆弱,这个答复似乎是,这种做法是更加脆弱。它依赖于执行定义的行为(具体地说,结构 va_list)是不保证保持相同。我认为,我的答案是正确的,我的提议的解决方案是少脆弱,因为它只能依靠定义特征的语言和框架。

对于那些需要迅速解决,这是一个扩展到这样做在迅速

extension String {

    static func stringWithFormat(format: String, argumentsArray: Array<AnyObject>) -> String {
        let arguments = argumentsArray.map { $0 as! CVarArgType }
        let result = String(format:format, arguments:arguments)
        return result
    }

}

是的,这是可能的。在海湾合作委员会针对Mac OS X,至少, va_list 是一个简单的C阵列,所以你会让一个 ids,然后告诉的是填补:

NSArray *argsArray = [[NSProcessInfo processInfo] arguments];
va_list args = malloc(sizeof(id) * [argsArray count]);
NSAssert1(args != nil, @"Couldn't allocate array for %u arguments", [argsArray count]);

[argsArray getObjects:(id *)args];

//Example: NSLogv is the version of NSLog that takes a va_list instead of separate arguments.
NSString *formatSpecifier = @"\n%@";
NSString *format = [@"Arguments:" stringByAppendingString:[formatSpecifier stringByPaddingToLength:[argsArray count] * 3U withString:formatSpecifier startingAtIndex:0U]];
NSLogv(format, args);

free(args);

你不应该依靠这种性质在代码,应该便携式。iPhone的开发,这是一件事,你绝对应该试验设备。

- (NSString *)stringWithFormat:(NSString *)format andArguments:(NSArray *)arguments {
    NSMutableString *result = [NSMutableString new];
    NSArray *components = format ? [format componentsSeparatedByString:@"%@"] : @[@""];
    NSUInteger argumentsCount = [arguments count];
    NSUInteger componentsCount = [components count] - 1;
    NSUInteger iterationCount = argumentsCount < componentsCount ? argumentsCount : componentsCount;
    for (NSUInteger i = 0; i < iterationCount; i++) {
        [result appendFormat:@"%@%@", components[i], arguments[i]];
    }
    [result appendString:[components lastObject]];
    return iterationCount == 0 ? [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] : result;
}

测试格式和参数:

NSString *format = @"xxx=%@, yyy=%@ last component";
NSArray *arguments = @[@"XXX", @"YYY", @"ZZZ"];

结果是:xxx XXX,yyy=YYY最后一部分

NSString *format = @"xxx=%@, yyy=%@ last component";
NSArray *arguments = @[@"XXX", @"YYY"];

结果是:xxx XXX,yyy=YYY最后一部分

NSString *format = @"xxx=%@, yyy=%@ last component";
NSArray *arguments = @[@"XXX"];

结果是:xxx XXX最后一部分

NSString *format = @"xxx=%@, yyy=%@ last component";
NSArray *arguments = @[];

结果是:最后一部分

NSString *format = @"some text";
NSArray *arguments = @[@"XXX", @"YYY", @"ZZZ"];

结果是:一些文字

我找到一些代码在网声称,这是可能的,但是我还没有做到它自己,但是如果你不知道这些参数提前你还需要建立的格式串的动态,所以我只是不明白这一点。

你最好只是建立的串通过迭代的阵列。

你可能会找到stringByAppendingString:或stringByAppendingFormat:实例的方法方便。

一个可以创建一个类别NSString和使功能,这接收到的格式,列和返回的串与替换为对象。

@interface NSString (NSArrayFormat)

+ (NSString *)stringWithFormat:(NSString *)format arrayArguments:(NSArray *)arrayArguments;

@end

@implementation NSString (NSArrayFormat)

+ (NSString *)stringWithFormat:(NSString *)format arrayArguments:(NSArray *)arrayArguments {
    static NSString *objectSpecifier = @"%@"; // static is redundant because compiler will optimize this string to have same address
    NSMutableString *string = [[NSMutableString alloc] init]; // here we'll create the string
    NSRange searchRange = NSMakeRange(0, [format length]);
    NSRange rangeOfPlaceholder = NSMakeRange(NSNotFound, 0); // variables are declared here because they're needed for NSAsserts
    NSUInteger index;
    for (index = 0; index < [arrayArguments count]; ++index) {
        rangeOfPlaceholder = [format rangeOfString:objectSpecifier options:0 range:searchRange]; // find next object specifier
        if (rangeOfPlaceholder.location != NSNotFound) { // if we found one
            NSRange substringRange = NSMakeRange(searchRange.location, rangeOfPlaceholder.location - searchRange.location);
            NSString *formatSubstring = [format substringWithRange:substringRange];
            [string appendString:formatSubstring]; // copy the format from previous specifier up to this one
            NSObject *object = [arrayArguments objectAtIndex:index];
            NSString *objectDescription = [object description]; // convert object into string
            [string appendString:objectDescription];
            searchRange.location = rangeOfPlaceholder.location + [objectSpecifier length]; // update the search range in order to minimize search
            searchRange.length = [format length] - searchRange.location;
        } else {
            break;
        }
    }
    if (rangeOfPlaceholder.location != NSNotFound) { // we need to check if format still specifiers
        rangeOfPlaceholder = [format rangeOfString:@"%@" options:0 range:searchRange];
    }
    NSAssert(rangeOfPlaceholder.location == NSNotFound, @"arrayArguments doesn't have enough objects to fill specified format");
    NSAssert(index == [arrayArguments count], @"Objects starting with index %lu from arrayArguments have been ignored because there aren't enough object specifiers!", index);
    return string;
}

@end

因为是创建在运行时,我们不能提供编制时警告,但我们可以使用NSAssert告诉我们,如果数量的说明等于数量目的内阵列。

创建了一个 项目 在审查在这个类别中都可以找到。还增加了Chuck的版本通过使用'stringByReplacingCharactersInRange:',加上一些测试。

利用一个万物体进入阵列,版本与'stringByReplacingCharactersInRange:'不规模很好(等待了大约2分钟然后关闭的应用程序).使用该版本与NSMutableString、功能做串大约4秒钟。该试验是使用模拟器。 使用前,测试应该可以做一个真正的设备(使用设备的最低specs).

编辑:在iPhone上5s的版本与NSMutableString需要10.471655s(一百万对象);在iPhone上5需要21.304876s.

这里的答案没有明确地建立一个阵列:

   NSString *formattedString = [NSString stringWithFormat:@"%@ World, Nice %@", @"Hello", @"Day"];

第一串是目标字符格式,下一串串待插入的目标。

不,你不能。变量的参数呼吁都解决了在编制时间,和你是有内容只能在运行时间。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top