2012-11-22 2 views
7

To różni się od innych wpisów "Czy mogę sprawdzić typ bloku" na SO, o ile mogę powiedzieć.Objective-C - sprawdź blok?

Chcę wiedzieć, czy, biorąc pod uwagę obiekt blokowy o nieznanym sygnaturze, mogę się dowiedzieć, jakie argumenty akceptuje przed wywołaniem?

Mam sytuację, w której mam wiele wywołań zwrotnych związanych z obiektami w słowniku. Chcę, żeby niektóre z tych wywołań oczekiwały innego zestawu argumentów. Przykład tutaj jest niezwykle uproszczony, ale myślę, że ma to rację.

Jak mogę się dowiedzieć, czy blok jest typu, który wcześniej wpisałem?

//MyClass.m 

// I start by declare two block types 
typedef void (^callbackWithOneParam)(NSString*); 
typedef void (^callbackWithTwoParams)(NSString*, NSObject*); 

........ 

// I create a dictionary mapping objects to callback blocks 

self.dict = @{ 
    @"name": "Foo", 
    @"callback": ^(NSString *aString) { 
    // do stuff with string 
    } 
}, { 
    @"name": "Bar", 
    @"callback": ^(NSString *aString, NSObject *anObject) { 
    // do stuff with string AND object 
    } 
} 

..... 

// Later, this method is called. 
// It looks up the "name" parameter in our dictionary, 
// and invokes the associated callback accordingly. 

-(void) invokeCallbackForName:(NSString*)name { 
    // What is the type of the result of this expression? 
    [self.dict objectForKey: name] 

    // I want to say: (pseudocode) 
    thecallback = [self.dict objectForKey: name]; 
    if (thecallback is of type "callbackWithOneParam") { 
     thecallback(@"some param") 
    } 
    else if (thecallback is of type "callbackWithTwoParams") { 
     thecallback(@"some param", [[NSObject alloc] init]); 
    } 
} 
+0

myślę, że mogę. W twojej sytuacji możesz po prostu zostawić dodatkowy parametr NSObject i wstawić zero, jeśli go nie używasz. – SAKrisT

+2

W tym przykładzie znacznie korzystniej jest użyć spójnego podpisu dla wszystkich bloków w słowniku. Kod w każdym bloku może następnie niezależnie decydować, które parametry będą używane lub ignorowane. Musisz również przesłać wartość zwracaną z '-objectForKey:' do swojej sygnatury bloku, zanim będziesz mógł wywołać blok. Musisz także skopiować każdy blok do sterty przed dodaniem go do słownika. – Darren

+0

Darren, mógłbyś rozwinąć swoje ostatnie dwa stwierdzenia, dziękuję! –

Odpowiedz

2

Szczerze mówiąc, jeśli wywołania zwrotne mają różne typy, powinny znajdować się pod różnymi kluczami. Dlaczego nie używać kluczy @"callbackWithOneParam" i @"callbackWithTwoParams"? Dla mnie jest to lepsze niż posiadanie ogólnego klawisza "oddzwaniania" i oddzielnego klawisza "typ", aby powiedzieć, jak interpretować wywołanie zwrotne.

Ale to, czego tak naprawdę wymaga, to używanie obiektów klas niestandardowych zamiast słowników. Przekroczyłeś granicę, w której obiekty ogólne przestają być wygodne i zaczynają powodować więcej problemów, niż rozwiązują.

+0

+1 do polecania niestandardowej klasy przez słownik. – BergQuester

-1

tylko sprawdzić, czy nazwa jest "Foo" lub "Bar"? będzie prawdopodobnie tak samo jak wiele wysiłku, jak sprawdzanie parametrów funkcji, które mam wrażenie isnt możliwe bez konieczności go w jakiejś formie klasy i będzie

if ([myObject class] == [MyClass class]) 
+0

naprawdę powinieneś użyć 'isKindOfClass:' lub 'isMemberClass:' w tym celu –

0

Dzwoniąc blok należy znać rodzaj argumentów. W twoim przypadku, jeśli "nazwa" nie jest wystarczająca do określenia, jakie argumenty powinny być, dodałbym inny klucz "typ", który mi powie. Oto przykład:

// Callback dictionary 

_callbacks = @{ 
    @{@"name":@"foo", @"type":@(1), @"callback":^(int i) { NSLog(@"%d", i); }}, 
    @{@"name":@"bar", @"type":@(2), @"callback":^(int i, int j) { NSLog(@"%d", i+j); }}, 
    @{@"name":@"zap", @"type":@(3), @"callback":^(int i, int j, int k) { NSLog(@"%d", i+j+k); }}, 
    @{@"name":@"cab", @"type":@(4), @"callback":^(NSString *s) { NSLog(@"%lu",s.length); }}, 
    @{@"name":@"fog", @"type":@(5), @"callback":^(void) { NSLog(@"I can't see"); }} 
} 


-(void) invokeCallbackForName:(NSString*)name withArguments:(NSArray*)args { 

    NSDictionary *info = _callbacks[name]; 

    if (info != nil) { 
     id block = info[@"callback"]; 
     int type = [info[@"type"] intValue]; 
     switch (type) { 

      case 1: { 
       int arg1 = [args[0] intValue]; 
       ((void(^)(int)) block)(arg1); 
       break; 
      } 
      case 2: { 
       int arg1 = [args[0] intValue]; 
       int arg2 = [args[1] intValue]; 
       ((void(^)(int,int)) block)(arg1,arg2); 
       break; 
      } 
      case 3: { 
       int arg1 = [args[0] intValue]; 
       int arg2 = [args[1] intValue]; 
       int arg3 = [args[2] intValue]; 
       ((void(^)(int,int,int)) block)(arg1,arg2,arg3); 
       break; 
      } 
      case 5: { 
       NSString *arg1 = [args[0] intValue]; 
       ((void(^)(NSString*)) block)(arg1); 
       break; 
      } 
      default: 
       [NSExceptien raise:NSInvalidArgumentException format:@"Unsupported callback type"]; 

     } 
    } 
} 

Należy pamiętać, że jest to konieczne, aby rzucić bloku do odpowiedniego typu w przeciwnym razie prawdopodobnie będzie mieć swój program awarii. Dzieje się tak dlatego, że blok zależy od kompilatora, aby umieścić argumenty na stosie w poprawnej kolejności i zezwalając na każdy typ zwracany.

0

Osobiście używam pomysłowy CTBlockDescription ...

CTBlockDescription pozwala kontrolować bloki w tym argumenty i skompilować możliwości czasowe na starcie.

BOOL(^bk)(BOOL,id) = ^BOOL(BOOL ani, id obj) { return YES; }; 

[CTBlockDescription.alloc initWithBlock:bk].blockSignature.description; 

<NSMethodSignature: 0x253f080> 
    number of arguments = 3 
    frame size = 12 
    is special struct return? NO 
    return value: -------- -------- -------- -------- 
     type encoding (c) 'c' 
     flags {isSigned} 
     modifiers {} 
     frame {offset = 0, offset adjust = 0, size = 4, size adjust = -3} 
     memory {offset = 0, size = 1} 
    argument 0: -------- -------- -------- -------- 
     type encoding (@) '@?' 
     flags {isObject, isBlock} 
     modifiers {} 
     frame {offset = 0, offset adjust = 0, size = 4, size adjust = 0} 
     memory {offset = 0, size = 4} 
    argument 1: -------- -------- -------- -------- 
     type encoding (c) 'c' 
     flags {isSigned} 
     modifiers {} 
     frame {offset = 4, offset adjust = 0, size = 4, size adjust = -3} 
     memory {offset = 0, size = 1} 
    argument 2: -------- -------- -------- -------- 
     type encoding (@) '@' 
     flags {isObject} 
     modifiers {} 
     frame {offset = 8, offset adjust = 0, size = 4, size adjust = 0} 
     memory {offset = 0, size = 4} 

Gorgeous ...