Comment puis-je passer un Block
à un Function
/ Method
?
J'ai essayé - (void)someFunc:(__Block)someBlock
en vain.
c'est à dire. Quel est le type pour un Block
?
Comment puis-je passer un Block
à un Function
/ Method
?
J'ai essayé - (void)someFunc:(__Block)someBlock
en vain.
c'est à dire. Quel est le type pour un Block
?
Réponses:
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 pointeur 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 compliqué. Vous pouvez à la place utiliser a typedef
pour rendre les types de blocs plus propres:
typedef void (^ IteratorBlock)(id, int);
Et puis passez ce bloc à une méthode comme celle-ci:
- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
NSNumber *
ou std::string&
ou tout autre élément que vous pourriez passer comme argument de fonction. C'est juste un exemple. (Pour un bloc équivalent à l'exception du remplacement id
par NSNumber
, le typedef
serait typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
.)
NS_NOESCAPE
, mais on enumerateObjectsUsingBlock
me dit qu'ils ne s'échappent pas, mais je ne vois NS_NOESCAPE
nulle part sur le site, et l'échappement n'est pas du tout mentionné dans les documents Apple. Pouvez-vous m'aider?
L'explication la plus simple de cette question est de suivre ces modèles:
1. Bloquer 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 comme 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 pour exécuter n'importe quoi en arrière-plan et sur 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);
}
Ensuite, importez blocks.h si nécessaire et invoquez-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 propriété simple si cela s'applique pour vous:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
assurez-vous que la propriété de bloc est "copie"!
et bien sûr, vous pouvez également utiliser typedef:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
Vous invoquez ou appelez également un bloc en utilisant la syntaxe habituelle de la fonction c
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Plus d'informations 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 cela aide quelqu'un :)
J'ai écrit un completionBlock pour une classe qui retournera les valeurs des dés après avoir été secoués:
Définissez typedef avec returnType ( déclaration .h
ci-dessus @interface
)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Définissez un @property
pour le bloc ( .h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Définissez une méthode avec finishBlock
( .h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Insérer méthode définie précédente dans le .m
fichier et engager finishBlock
à @property
définir avant
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Pour déclencher, completionBlock
passez-lui un type de variable prédéfini (n'oubliez pas de vérifier si le paramètre completionBlock
existe)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
Malgré les réponses données sur ce fil, j'ai vraiment eu du mal à écrire une fonction qui prendrait un Block comme 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, chargerait certaines données JSON de cette URL sur un thread d'arrière-plan, puis retournerait un NSArray * de résultats à la fonction appelante.
Fondamentalement, je voulais garder toute la complexité du thread d'arrière-plan cachée 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 c'est le bit avec lequel j'ai eu du mal: comment le déclarer, et comment le faire appeler la fonction Block une fois les données chargées, et passer le Block
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 StackOverflow concerne la façon d'appeler des fonctions, en passant un bloc en tant que paramètre, j'ai donc simplifié le code ci-dessus et non inclus la loadJSONDataFromURL
fonction.
Mais, si vous êtes intéressé, 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!)
Le modèle complet ressemble à
- (void) main {
//Call
[self someMethodWithSuccessBlock:^{[self successMethod];}
withFailureBlock:^(NSError * error) {[self failureMethod:error];}];
}
//Definition
- (void) someMethodWithSuccessBlock:(void (^) (void))successBlock
withFailureBlock:(void (^) (NSError*))failureBlock {
//Execute a block
successBlock();
// failureBlock([[NSError alloc]init]);
}
- (void) successMethod {
}
- (void) failureMethod:(NSError*) error {
}