16

trzeba utworzyć w aplikacji iOS fałszywy va_list przejść do NSString initWithFormat:arguments: funkcji, to jest mój kod:fałszywy va_list w ARC

NSArray *fixedArguments = [[NSArray alloc] initWithArray:arguments]; 

NSRange range = NSMakeRange(0, [fixedArguments count]); 

va_list fakeArgList = (va_list)malloc(sizeof(NSString *) * [fixedArguments count]); 

__unsafe_unretained id *ptr = (__unsafe_unretained id *)fakeArgList; 

[fixedArguments getObjects:ptr range:range]; 

content = [[NSString alloc] initWithFormat:outputFormat 
              arguments:(va_list)fakeArgList]; 
free(fakeArgList); 

Kompilator narzeka z tej wiadomości na linii Obsada:

error: cast of a non-Objective-C pointer type 'va_list' (aka 'char *') to '__unsafe_unretained id *' is disallowed with ARC 

funkcja getObjects:range: jest zdefiniowany w następujący sposób:

- (void)getObjects:(id __unsafe_unretained [])objects range:(NSRange)range; 

Próbowałem już wszystkiego, ale nadal nie mogę pozbyć się tego błędu ...

Czy istnieje rozwiązanie do tworzenia fałszywego va_list z włączonym ARC? Co ja robię źle?

Odpowiedz

30

EDYTOWANIE: To już nie działa. Jak przewidziano w początkowej odpowiedzi, wydaje się, że ABI zmieniło się pod tą odpowiedzią:

Zagrałem trochę i uruchomiłem - Podwójnie sprawdziłem pod kątem przecieków lub porzuconej pamięci i nie widziałem żadnych.

NSArray *fixedArguments = [[NSArray alloc] initWithObjects: @"foo", @"bar", @"baz", nil]; 

    NSRange range = NSMakeRange(0, [fixedArguments count]); 

    NSMutableData* data = [NSMutableData dataWithLength: sizeof(id) * [fixedArguments count]];  

    [fixedArguments getObjects: (__unsafe_unretained id *)data.mutableBytes range:range]; 

    NSString* content = [[NSString alloc] initWithFormat: @"1: %@ 2: %@ 3: %@" arguments: data.mutableBytes]; 

    NSLog(@"%@", content); 

lubię (ab) używać NSMutableData tak, aby zachować/semantykę zwalniające na dowolnym fragmencie pamięci - To niekoniecznie w oparciu o wydane pod ręką, ale jest to schludny mały trick.

Uwaga dla przyszłych czytelników: Podkręcanie va_list tak działa z obecną ABI dla MacOS i iOS, ale ogólnie nie jest przenośne i nie jest dobrym podejściem.

+0

Dziękuję bardzo ... buduję widok kompletacji że dany plist zawierał szereg słowników, format wydruku, powiedzmy ' "(% @ -% @)% @"' , a lista kluczy zapełnia widok selektora sformatowanym ciągiem wyodrębniającym dane z pliku plist. Jedynym sposobem na użycie sformatowanego wydruku ze zmienną listą argumentów było sfałszowanie va_list. Wiem, że to dalekie od czystego programowania, ale nie mogłem wymyślić lepszego rozwiązania, każda ważna alternatywa jest naprawdę mile widziana i myślę, że opublikuję kolejne pytanie dotyczące mojego problemu, aby znaleźć bardziej przejrzyste rozwiązanie. – Scakko

+0

Jeśli zawsze pracujesz z% @ i nigdy innymi parametrami, możesz po prostu wyszukać wystąpienia% @ w ciągu znaków i zastąpić je [opisem obiektu] dla każdego z parametrów. Ten sam efekt, bez fałszywej va_list. Ale to nie zadziała w przypadku formatowania liczbowego ani niczego innego, chyba że chcesz zrobić dużo dodatkowej pracy. – ipmcc

+0

Spróbuję dzisiaj, dziękuję bardzo ... – Scakko

0

Jest to możliwe, jeśli chcesz dodać trochę swobody do swojego projektu!

Ważnym bitem jest mapowanie NSArray na [CVarArgType], które jest szybkim odpowiednikiem dla va_list. Jeśli spróbujesz rzucić [AnyObject] do [CVarArgType] powodujesz awarie czasu uruchamiania, ale z map możemy jawnie utworzyć potrzebną listę.

Reszta kodu to opakowanie, które zrobiłem, aby móc wywołać to z obiektu obj-c. Możesz utworzyć wrapper dla dowolnej funkcji obj-c, którą chcesz wywołać w ten sposób.

@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) 
    } 
}