2017-03-10 4 views
0

Ich habe eine Methode geschrieben, die es einfach macht, den Pfad-Teil einer URL mit Formatzeichenfolgen festzulegen. Ursprünglich habe ich nur die Formatzeichenfolge und Argumente direkt in initWithFormat: übergeben, aber jemand, der mich übergibt, argumentiert mit Leerzeichen innerhalb. Ich änderte die Methode, um die Argumente in Prozent zu kodieren, bevor ich zu initWithFormat: ging.Wie kann ich diese varargs Funktion sicher handhaben?

Ich könnte es wie [request setUrlWithFormat:@"users/%@/timesheets", username], wo username könnte bmauter oder b mauter sein.

- (void) setUrlWithFormat:(NSString *)format, ... { 

    // loop through varargs and cleanse them for the URL path 
    va_list args; 
    va_start(args, format); 
    NSMutableArray *cleaned = [[NSMutableArray alloc] init]; 
    for(NSString *s = format; s != nil; s = va_arg(args, NSString *)) { 
     if (s == format) continue; 
     [cleaned addObject:[s stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]; 
    } 
    va_end(args); 

    // put the cleansed values back into a varargs list 
    __unsafe_unretained id *argList = (__unsafe_unretained id *) calloc(1UL, sizeof(id) * cleaned.count); 
    for (NSInteger i = 0; i < cleaned.count; i++) argList[i] = cleaned[i]; 
    NSString* result = [[NSString alloc] initWithFormat:format, *argList]; 
    free(argList); 

    [self setUrl:result]; 
} 

Manchmal mit einem EXC_BAD_ACCESS auf der ersten for Loopleitung ich zum Absturz bringen. Manchmal stürze ich auf die initWithString: Linie. Meistens funktioniert es perfekt.

Update: Danke wieder @uliwitness. Falls jemand will sonst sehen, was ich am Ende mit auf, geht hier:

- (void) setUrlWithFormat:(NSString *)format, ... { 

    DLog(@"format=%@", format); 

    va_list args; 
    va_start(args, format); 

    NSMutableString *result = [format mutableCopy]; 

    NSRange range = [result rangeOfString:@"%@"]; 
    while(range.location != NSNotFound) { 

     NSObject *obj = va_arg(args, NSObject *); 
     NSString *dirty = nil; 
     if ([obj isKindOfClass:[NSString class]]) dirty = (NSString *)obj; 
     else dirty = [NSString stringWithFormat:@"%@", obj]; 

     NSString *clean = [dirty stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]; 
     DLog(@"dirty=%@, clean=%@", dirty, clean); 

     [result replaceCharactersInRange:range withString:clean]; 

     range = [result rangeOfString:@"%@"]; 
    } 
    va_end(args); 

    DLog(@"result=%@", result); 

    [self setUrl:result]; 
} 

Antwort

2

Sie machen hier einige Annahmen, die einfach nicht wahr sind. Zum einen übergibst du NIL nicht als Vararg, also kannst du nicht davon ausgehen, dass es ein NIL geben wird. Stattdessen müssen Sie die Anzahl der Formatplatzhalter zählen und nur so viele Varargs erfassen.

Was Sie gerade machen, ist das Ende der Argumentliste. Manchmal hast du Glück, und der zufällige Speicher hinter dieser Liste stellt sich heraus, dass er 8 Null Bytes hat, und so sieht es wie NIL aus und deine Schleife endet. Wenn es abstürzt, erhalten Sie einige andere zufällige Bytes, die kein gültiger Objektzeiger sind, oder wie ein Zeiger auf eine andere Klasse von Objekten aussehen, weshalb Sie abstürzen.

Auch, warum gehen Sie davon aus, dass Sie einfach das erste Element eines Arrays in NSString -stringWithFormat übergeben können:? Ihr Code funktioniert in diesem Testfall, aber nur, weil Sie nur einen Formatplatzhalter haben.

-initWithFormat: verwendet eine übereinstimmende Anzahl von Parametern für die Formatzeichenfolge. Nicht eine andere Zahl (wie Sie jetzt übergeben), und kein Array (das Sie wahrscheinlich denken, dass Sie übergeben, aber da Arrays in C sind Zeiger auf das erste Element, und C kann nicht einen Zeiger auf ein Objekt von a Zeiger auf ein Array, und weiß nicht, wie lang das Array ist, alles, was Sie tatsächlich passieren, ist ein Element).

Um dies zu tun, müssen Sie Ihre eigene Version des Formatzeichenfolgen-Parsings schreiben. Eine schnelle und unreine Version wäre es, rangeOfString zu verwenden, um die "%@" s zu finden, und dann den Teil davor an die Zeichenkette anzuhängen, dann das entgangene entsprechende Argument, und sobald Ihre Schleife endet, was auch immer in der Zeichenkette übrig ist.

+0

Vielen Dank, Sie haben Recht. Kurz nachdem ich gepostet hatte, fand ich heraus, dass ich die Anzahl der Formatspezifizierer zählen sollte. Zu wissen, dass alle Abstürze, die ich bisher gefunden habe, auf der ersten "for" -Schleife gelöst werden. Ich stehe immer noch auf dem zweiten 'for' Loop-Crash. Dieser Code versucht, ein 'NSArray' in eine Varargs-Liste zu packen. Ich habe diesen Code hier auf SO gefunden und für meine Zwecke optimiert. Im Idealfall hätte 'NSString' einen' initWithFormat: argsInArray': Typ der Methode. Ich nehme an, du hast auch recht, dass ich bis dahin die Formatspezifizierer einfach selbst ersetzen muss. – bmauter

Verwandte Themen