2013-04-18 7 views
7

NSInvocation 's metoda -retainArguments jest przydatna, gdy nie uruchamia się natychmiast NSInvocation, ale zrób to później; zachowuje argumenty obiektu, dzięki czemu zachowują ważność w tym czasie.Czy - [NSInvocation retainArguments] kopiować bloki?

Jak wszyscy wiemy, argumenty bloków powinny zostać skopiowane zamiast zachowane. Moje pytanie brzmi, czy -retainArguments wiedzieć, aby skopiować zamiast zachować argument, gdy jest to rodzaj bloku? Dokumentacja nie wskazuje, że tak jest, ale wydaje się, że jest to łatwe i rozsądne.

Aktualizacja: zachowanie wydaje się zmieniły w iOS 7. I tylko przetestowane i w iOS 6.1 i wcześniej, -retainArguments nie kopiować parametry typu bloku. W systemie iOS 7 i nowszych wersjach -retainArguments kopiuje parametry typu bloku. Dokumentacja -retainArguments została zaktualizowana, aby powiedzieć, że kopiuje bloki, ale nie mówi, kiedy zachowanie się zmieniło (co jest naprawdę niebezpieczne dla osób, które obsługują starsze systemy operacyjne).

+0

Dzięki za aktualizację! – matt

Odpowiedz

1

nr

Obraz jeśli odpowiedź brzmi tak, to gdzie NSInvocation jest wystarczająco inteligentny, aby skopiować blok, powinien zrobić coś takiego:

for (/*every arguments*/) { 
    if (/*arg is object. i.e. @encode(arg) is '@'*/) { 
     if ([arg isKindOfClss:[NSBlock class]]) { 
      arg = [arg copy]; // copy block 
     } else { 
      [arg retain]; 
     } 
    } 
} 

problemem jest to, że arg jest modyfikowany podczas kopiowania blok, co nie powinno się zdarzyć, ponieważ oznacza to, że wywołanie retainArguments może zmienić argumenty w NSInvocation. to złamie wiele założeń, które już zostały wprowadzone. (Tj argumenty dostać od NSInvocation powinny być takie same jak argumenty używane do tworzenia NSInvocation) Aktualizacja


właśnie zrobiłem test w celu dostosowania odpowiedź jest NO, ale mój poprzedni punkt był niepoprawny chociaż ...

@interface Test : NSObject 

@end 

@implementation Test 

- (void)testMethodWithBlock:(void (^)(void))block obj:(id)obj cstr:(const char *)cstr { 
    NSLog(@"%p %p %p %@", block, obj, cstr, [block class]); 
} 

@end 

@implementation testTests 

- (void)test1 { 
    __block int dummy; 
    Test *t = [[Test alloc] init]; 
    NSMethodSignature *ms = [t methodSignatureForSelector:@selector(testMethodWithBlock:obj:cstr:)]; 
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:ms]; 
    void (^block)(void) =^{ 
     dummy++; // stop this become global block 
    }; 
    id obj = @"object"; 
    char *cstr = malloc(5); 
    strcpy(cstr, "cstr"); 


    NSLog(@"%@", [ms debugDescription]); 

    NSLog(@"%p %p %p %@", block, obj, cstr, [block class]); 

    [invocation setSelector:@selector(testMethodWithBlock:obj:cstr:)]; 
    [invocation setArgument:&block atIndex:2]; 
    [invocation setArgument:&obj atIndex:3]; 
    [invocation setArgument:&cstr atIndex:4]; 

    [invocation invokeWithTarget:t]; 

    [invocation retainArguments]; 

    [invocation invokeWithTarget:t]; 

    free(cstr); 
} 

@end 

wyjście, ARC wyłączony (i awarii):

2013-04-18 19:49:27.616 test[94555:c07] 0xbfffe120 0x70d2254 0x7167980 __NSStackBlock__ 
2013-04-18 19:49:27.617 test[94555:c07] 0xbfffe120 0x70d2254 0x7167980 __NSStackBlock__ 
2013-04-18 19:49:27.618 test[94555:c07] 0xbfffe120 0x70d2254 0x736a810 __NSStackBlock__ 

ARC włączona:

2013-04-18 19:51:03.979 test[95323:c07] 0x7101e10 0x70d2268 0x7101aa0 __NSMallocBlock__ 
2013-04-18 19:51:03.979 test[95323:c07] 0x7101e10 0x70d2268 0x7101aa0 __NSMallocBlock__ 
2013-04-18 19:51:03.980 test[95323:c07] 0x7101e10 0x70d2268 0xe0c1310 __NSMallocBlock__ 

jak widać, c ciąg są kopiowane przez retainArguments ale nie bloki. ale przy włączonym ARC problem powinien zniknąć, ponieważ ARC skopiował go w pewnym momencie.

+0

Kopiowanie bloku nie zmienia tego, na czym polega system typu, podpis bloku. Oczywiście, może on przejść z '__NSStackBlock__' do' __NSHeapBlock__', ale to jest punkt kopiowania bloku: przeniesienie go do sterty, aby przedłużyć jego żywotność. – CodaFi

+0

Dobra uwaga. Jednak -retainArguments' również kopiuje łańcuchy C. A kopiowanie ciągów C także zmienia argumenty. – user102008

+0

@CodaFi mój punkt to 'blok! = [Blokuj kopię]', więc może to spowodować problem, jeśli jest skopiowany –

4

Z pewnością ma (aczkolwiek sam tego nie przetestowałem). Według documentation:

retainArguments

Jeśli odbiornik nie zrobiono, zachowuje cel i wszystkie argumenty Przedmiot odbiornika oraz kopie wszystkich swoich argumentów C-strunowych i bloków.

  • (void) retainArguments

dyskusyjne

zanim metoda ta jest wywoływana, argumentsRetained nie zwraca; po, zwraca TAK.

Dla wydajności nowo utworzone obiekty NSInvocation nie zachowują się lub kopiują swoje argumenty, nie zachowują swoich celów, nie kopiują ciągów C ani nie kopiują powiązanych bloków. Powinieneś polecić obiekt NSInvocation , aby zachować jego argumenty, jeśli zamierzasz je buforować, , ponieważ w przeciwnym wypadku argumenty mogą zostać zwolnione przed wywołaniem wywołania . Obiekty NSTimer zawsze instruują wywoływania, aby zachowywały swoje argumenty, na przykład, ponieważ zazwyczaj występuje opóźnienie, zanim nastąpi uruchomienie timera.

+0

Interesujący. Dokumentacja została zmieniona: https://web.archive.org/web/20120826185131/http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html # // apple_ref/occ/instm/NSInvocation/retainArguments Ale nie dokumentuje, czy i kiedy zmieniło się zachowanie. Bloki z pewnością nie zostały skopiowane przez 'retainArguments' kiedy wypróbowałem to w iOS 6 rok temu. – user102008

+1

Właśnie go przetestowałem i jest on kopiowany w systemie iOS 7, ale nie w systemie iOS 6.1, więc został zmieniony. – user102008

+0

Powinny one udokumentować zmianę jako aktualizację ios 7, w przeciwieństwie do tego, że wydaje się, że zawsze było tak ... Głupie jabłko, sztuczki są dla dzieci! –