Comment puis-je passer une Block
à une Function
Method
?
J'ai essayé - (void)someFunc:(__Block)someBlock
sans succès.
c'est à dire. Quel est le _/type pour une Block
?
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;
Cela pourrait être utile:
- (void)someFunc:(void(^)(void))someBlock;
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
};
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);
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
});
});
}
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;
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
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 :)
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!)
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:
Définir typedef avec returnType (.h
ci-dessus __ déclaration@interface
)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Définir un @property
pour le bloc (.h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Définir une méthode avec finishBlock
(.h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
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;
}
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);
}
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 {
}