2011-09-06 1 views
7

Ceci est une extension de ce queston: Is it possible to create a category of the "Block" object in Objective-C.Un bloc Obj-C peut-il s'exécuter?

Fondamentalement, alors qu'il semble possible de créer une catégorie sur des blocs, soit par NSObject ou NSBlock, je vais avoir du mal à comprendre comment le bloc serait en mesure de s'évaluer. L'exemple donné dans la réponse à la dernière question:

- (void) doFoo { 
    //do something awesome with self, a block 
    //however, you can't do "self()". 
    //You'll have to cast it to a block-type variable and use that 
} 

Implique qu'il est possible de jeter en quelque sorte auto à une variable de bloc, mais comment peut-on exécuter le bloc lui-même? Par exemple, dire que je l'ai fait une catégorie sur NSBlock et une méthode ne:

NSBlock* selfAsBlock = (NSBlock*)self; 

Est-il un message que je peux envoyer à selfAsBlock avoir le bloc d'évaluer?

+0

Oui, vous pouvez. Vous pouvez trouver plus dans ce (déjà sur SO) http://stackoverflow.com/questions/4824613/is-there-a-self-pointer-for-blocks –

+0

Je ne vois pas comment cela résout ce problème. Cette réponse semble se concentrer sur la façon d'appeler un bloc à l'intérieur de sa propre définition de bloc. Je parle d'évaluer un bloc de l'objet bloc lui-même. Juste pour être un peu plus précis, ce que j'espère accomplir avec ceci est d'être capable d'ajouter des méthodes aux blocs (soit sur NSObject ou NSBlock) pour faire un flux de contrôle basé sur un bloc (ie block [block whileTrueDo: block]). Pour ce faire, j'ai besoin du bloc pour se réévaluer dans la méthode. – donalbain

Répondre

7

Implique qu'il est possible de jeter en quelque sorte auto à un bloc variable

Comme ceci:

- (void)doFoo { 
    // Assume the block receives an int, returns an int, 
    // and cast self to the corresponding block type 
    int (^selfBlock)(int) = (int (^)(int))self; 

    // Call itself and print the return value 
    printf("in doFoo: %d\n", selfBlock(42)); 
} 

Notez que (dans la plupart des cas), vous devez fixer la signature de bloc afin que le compilateur est capable de configurer le site d'appel en fonction de la plate-forme cible ABI. Dans l'exemple ci-dessus, la signature est le type de retour int, paramètre unique de type int.

Un exemple complet est:

#import <Foundation/Foundation.h> 
#import <objc/runtime.h> 

@interface Foo : NSObject 
- (void)doFoo; 
@end 

@implementation Foo 
- (void)doFoo { 
    // Assume the block receives an int, returns an int, 
    // and cast self to the corresponding block type 
    int (^selfBlock)(int) = (int (^)(int))self; 

    // Call itself and print the return value 
    printf("in doFoo: %d\n", selfBlock(42)); 
} 
@end 

int main(void) { 
    [NSAutoreleasePool new]; 

    // From Dave's answer 
    Method m = class_getInstanceMethod([Foo class], @selector(doFoo)); 
    IMP doFoo = method_getImplementation(m); 
    const char *type = method_getTypeEncoding(m); 
    Class nsblock = NSClassFromString(@"NSBlock"); 
    class_addMethod(nsblock, @selector(doFoo), doFoo, type); 

    // A block that receives an int, returns an int 
    int (^doubler)(int) = ^int(int someNumber){ return someNumber + someNumber; }; 

    // Call the category method which in turn calls itself (the block) 
    [doubler doFoo]; 

    return 0; 
} 
+0

Super. Le code d'ajout de méthode dynamique explicite est-il nécessaire? Ou cela peut-il aussi être fait à travers une catégorie? – donalbain

+1

@don Comme expliqué dans les réponses à la question que vous avez liée, je ne pense pas qu'il soit possible d'utiliser une catégorie puisque le compilateur a besoin de la déclaration d'interface d'origine lors de l'analyse d'une catégorie. –

4

NSBlock a une méthode invoke qui peut être utilisée pour appeler le bloc.

NSBlock* b = ^() { /* do stuff */ }; 
[b invoke]; 

Notez qu'il s'agit d'une méthode privée non documentée.

+1

C'est le genre de méthode que je cherchais. Bien sûr, je suppose que tout ce que je fais ici est dangereux puisque NSBlock est privé et pourrait changer. – donalbain

+0

Si vous souhaitez appeler le bloc, vous pouvez simplement faire ce block(); au lieu de [invoquer un bloc]; https://stackoverflow.com/a/9484268 – unmircea