web-dev-qa-db-fra.com

Bloc de passe Objective-C en tant que paramètre

Comment puis-je passer une Block à une FunctionMethod?

J'ai essayé - (void)someFunc:(__Block)someBlock sans succès.

c'est à dire. Quel est le _/type pour une Block?

133
Jacksonkr

Le type d'un bloc varie en fonction de ses arguments et de son type de retour. Dans le cas général, les types de bloc sont déclarés de la même manière que les types de pointeurs de fonction, mais en remplaçant le * par un ^. Une façon de passer un bloc à une méthode est la suivante:

- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;

Mais comme vous pouvez le voir, c'est désordonné. Vous pouvez également utiliser une typedef pour rendre les types de blocs plus propres:

typedef void (^ IteratorBlock)(id, int);

Et passez ensuite ce bloc à une méthode comme celle-ci:

- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
244
Jonathan Grynspan

Cela pourrait être utile:

- (void)someFunc:(void(^)(void))someBlock;
50
quaertym

L'explication la plus simple pour cette question est la suivante:

1. Bloc comme paramètre de méthode

Modèle

- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
        // your code
}

Exemple

-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
        // your code
}

Autre utilisation des cas:

2. Bloquer en tant que propriété

Modèle

@property (nonatomic, copy) returnType (^blockName)(parameters);

Exemple

@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);

3. Bloquer comme argument de méthode

Modèle

[anObject aMethodWithBlock: ^returnType (parameters) {
    // your code
}];

Exemple

[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
    // your code
}];

4. Bloquer en tant que variable locale

Modèle

returnType (^blockName)(parameters) = ^returnType(parameters) {
    // your code
};

Exemple

void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
    // your code
};

5. Bloquer en tant que typedef

Modèle

typedef returnType (^typeName)(parameters);

typeName blockName = ^(parameters) {
    // your code
}

Exemple

typedef void(^completionBlock)(NSArray *array, NSError *error);

completionBlock didComplete = ^(NSArray *array, NSError *error){
    // your code
};
45
EnriMR

Vous pouvez faire comme ceci, en passant block comme paramètre de bloc:

//creating a block named "completion" that will take no arguments and will return void
void(^completion)() = ^() {
    NSLog(@"bbb");
};

//creating a block namd "block" that will take a block as argument and will return void
void(^block)(void(^completion)()) = ^(void(^completion)()) {
    NSLog(@"aaa");
    completion();
};

//invoking block "block" with block "completion" as argument
block(completion);
22
Aleksei Minaev

Une autre façon de passer un bloc en utilisant les fonctions с dans l'exemple ci-dessous. J’ai créé des fonctions permettant d’exécuter quoi que ce soit en arrière-plan et dans la file d’attente principale.

fichier blocks.h

void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));

fichier blocks.m

#import "blocks.h"

void performInBackground(void(^block)(void)) {
    if (nil == block) {
        return;
    }

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), block);
}

void performOnMainQueue(void(^block)(void)) {
    if (nil == block) {
        return;
    }

    dispatch_async(dispatch_get_main_queue(), block);
}

Puis importez blocks.h si nécessaire et appelez-le:

- (void)loadInBackground {

    performInBackground(^{

        NSLog(@"Loading something in background");
        //loading code

        performOnMainQueue(^{
            //completion hadler code on main queue
        });
    });
}
7
Dren

Vous pouvez également définir block comme une simple propriété si elle vous concerne:

@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);

assurez-vous que cette propriété de bloc est "copie"!

et bien sûr vous pouvez aussi utiliser typedef:

typedef void (^SimpleBlock)(id);

@property (nonatomic, copy) SimpleBlock someActionHandler;
6
iiFreeman

De plus, vous appelez ou appelez un bloc en utilisant la syntaxe habituelle de la fonction c

-(void)iterateWidgets:(IteratorBlock)iteratorBlock{

    iteratorBlock(someId, someInt);
}

Plus d'infos sur les blocs ici

http://developer.Apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxGettingStarted.html#// Apple_ref/doc/uid/TP40007502-CH7-SW1

5
gheese

J'ai toujours tendance à oublier la syntaxe des blocs. Cela me vient toujours à l’esprit lorsque je dois déclarer un blocage. J'espère que ça aide quelqu'un :)

http://fuckingblocksyntax.com

3
Juan F. Sagasti

Malgré les réponses données sur ce fil, j'ai eu beaucoup de mal à écrire une fonction qui prendrait un bloc en tant que fonction - et avec un paramètre. Finalement, voici la solution que j'ai trouvée.

Je voulais écrire une fonction générique, loadJSONthread, qui prendrait l'URL d'un service Web JSON, chargeait des données JSON à partir de cette URL sur un fil de travail en arrière-plan, puis renvoyait un NSArray * de résultats à la fonction d'appel.

En gros, je voulais cacher toute la complexité du thread d'arrière-plan dans une fonction générique réutilisable.

Voici comment j'appellerais cette fonction:

NSString* WebServiceURL = @"http://www.inorthwind.com/Service1.svc/getAllCustomers";

[JSONHelper loadJSONthread:WebServiceURL onLoadedData:^(NSArray *results) {

    //  Finished loading the JSON data
    NSLog(@"Loaded %lu rows.", (unsigned long)results.count);

    //  Iterate through our array of Company records, and create/update the records in our SQLite database
    for (NSDictionary *oneCompany in results)
    {
        //  Do something with this Company record (eg store it in our SQLite database)
    }

} ];

... et voici le problème avec lequel j'ai eu du mal: comment le déclarer et comment l'obtenir pour appeler la fonction Block une fois les données chargées et passer le Block un NSArray * des enregistrements chargés:

+(void)loadJSONthread:(NSString*)urlString onLoadedData:(void (^)(NSArray*))onLoadedData
{
    __block NSArray* results = nil;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{

        // Call an external function to load the JSON data 
        NSDictionary * dictionary = [JSONHelper loadJSONDataFromURL:urlString];
        results = [dictionary objectForKey:@"Results"];

        dispatch_async(dispatch_get_main_queue(), ^{

            // This code gets run on the main thread when the JSON has loaded
            onLoadedData(results);

        });
    });
}

Cette question de StackOverflow concerne la procédure à suivre pour appeler des fonctions, en passant un bloc en tant que paramètre. J'ai donc simplifié le code ci-dessus sans inclure la fonction loadJSONDataFromURL

Mais, si cela vous intéresse, vous pouvez trouver une copie de cette fonction de chargement JSON sur ce blog: http://mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm

J'espère que cela aidera d'autres développeurs XCode! (N'oubliez pas de voter pour cette question et ma réponse, si c'est le cas!)

2
Mike Gledhill

J'ai écrit pour la classe d'achèvement un verrou qui retournera les valeurs des dés après qu'ils aient été secoués:

  1. Définir typedef avec returnType (.h ci-dessus __ déclaration@interface)

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
    
  2. Définir un @property pour le bloc (.h)

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
    
  3. Définir une méthode avec finishBlock (.h)

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
    
  4. Insérer la méthode précédemment définie dans le fichier .m et valider finishBlock dans @property défini avant

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
    
  5. Pour déclencher completionBlock, transmettez-lui le type de variable prédéfini (N'oubliez pas de vérifier si la completionBlock existe)

    if( self.completeDiceRolling ){
        self.completeDiceRolling(self.dieValue);
    }
    
1
Alex Cio

Le modèle complet ressemble à

- (void) main {
    [self someMethodWithSuccessBlock:^{[self successMethod];}
                    withFailureBlock:^(NSError * error) {[self failureMethod:error];}];
}

- (void) someMethodWithSuccessBlock:(void (^) (void))successBlock
                   withFailureBlock:(void (^) (NSError*))failureBlock {

    //Execute a block
    successBlock();
//    failureBlock([[NSError alloc]init]);

}

- (void) successMethod {

}

- (void) failureMethod:(NSError*) error {

}
0
yoAlex5