Wie kann ich eine Block
an eine Function
/Method
übergeben?
Ich habe - (void)someFunc:(__Block)someBlock
ohne Erfolg versucht.
dh. Was ist der Typ für eine Block
?
Der Typ eines Blocks hängt von seinen Argumenten und seinem Rückgabetyp ab. Im Allgemeinen werden Blocktypen genauso deklariert wie Funktionszeigertypen, jedoch wird *
durch einen ^
ersetzt. Eine Methode zum Übergeben eines Blocks an eine Methode ist wie folgt:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Aber wie Sie sehen können, ist das chaotisch. Sie können stattdessen eine typedef
verwenden, um Blocktypen sauberer zu machen:
typedef void (^ IteratorBlock)(id, int);
Und dann übergeben Sie diesen Block an eine Methode wie folgt:
- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
Das könnte hilfreich sein:
- (void)someFunc:(void(^)(void))someBlock;
Die einfachste Erklärung für diese Frage lautet wie folgt:
1. Block als Methodenparameter
Vorlage
- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
// your code
}
Beispiel
-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
// your code
}
Andere Verwendung von Fällen:
2. Als Eigenschaft blockieren
Vorlage
@property (nonatomic, copy) returnType (^blockName)(parameters);
Beispiel
@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);
3. Blockieren als Methodenargument
Vorlage
[anObject aMethodWithBlock: ^returnType (parameters) {
// your code
}];
Beispiel
[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
// your code
}];
4. Block als lokale Variable
Vorlage
returnType (^blockName)(parameters) = ^returnType(parameters) {
// your code
};
Beispiel
void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
// your code
};
5. Block als typedef
Vorlage
typedef returnType (^typeName)(parameters);
typeName blockName = ^(parameters) {
// your code
}
Beispiel
typedef void(^completionBlock)(NSArray *array, NSError *error);
completionBlock didComplete = ^(NSArray *array, NSError *error){
// your code
};
Sie können dies so tun, indem Sie einen Block als Blockparameter übergeben:
//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);
Eine weitere Möglichkeit, Block mit с-Funktionen zu übergeben, ist im Beispiel unten angegeben. Ich habe Funktionen erstellt, um im Hintergrund und in der Hauptwarteschlange alles auszuführen.
datei blocks.h
void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));
blocks.m Datei
#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);
}
Dann importiere blocks.h wenn nötig und rufe es auf:
- (void)loadInBackground {
performInBackground(^{
NSLog(@"Loading something in background");
//loading code
performOnMainQueue(^{
//completion hadler code on main queue
});
});
}
Sie können block auch als einfache Eigenschaft festlegen, wenn dies für Sie zutrifft:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
stellen Sie sicher, dass die Block-Eigenschaft "copy" ist!
und natürlich können Sie auch typedef verwenden:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
Sie rufen auch einen Block mit der üblichen c-Funktionssyntax auf
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Mehr Infos zu den Blöcken hier
Ich neige immer dazu, die Block-Syntax zu vergessen. Das fällt mir immer ein, wenn ich eine Blockade erklären muss. Ich hoffe es hilft jemandem :)
Trotz der Antworten in diesem Thread hatte ich wirklich Schwierigkeiten, eine Funktion zu schreiben, die einen Block als Funktion und mit einem Parameter verwenden würde. Schließlich ist hier die Lösung, die ich mir ausgedacht habe.
Ich wollte eine generische Funktion, loadJSONthread
, schreiben, die die URL eines JSON-Web-Service übernehmen würde, einige JSON-Daten von dieser URL in einen Hintergrundthread laden und dann ein NSArray * der Ergebnisse an die aufrufende Funktion zurückgeben würde.
Grundsätzlich wollte ich die Hintergrund-Thread-Komplexität in einer generischen, wiederverwendbaren Funktion verborgen halten.
So würde ich diese Funktion nennen:
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)
}
} ];
... und dies ist das Bit, mit dem ich zu kämpfen hatte: wie man es deklariert und wie man die Block-Funktion aufruft, wenn die Daten geladen wurden, und die Variable Block
ein NSArray * der geladenen Datensätze übergeben wird:
+(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);
});
});
}
Diese StackOverflow-Frage bezieht sich auf das Aufrufen von Funktionen, wobei ein Block als Parameter übergeben wird. Daher wurde der Code oben vereinfacht und die loadJSONDataFromURL
-Funktion nicht eingeschlossen.
Bei Interesse finden Sie jedoch eine Kopie dieser JSON-Ladefunktion in diesem Blog: http://mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm
Hoffe, das hilft einigen anderen XCode-Entwicklern! (Vergessen Sie nicht, diese Frage und meine Antwort abzustimmen, falls dies der Fall ist!)
Ich habe einen CompletionBlock für eine Klasse geschrieben, der die Werte von Würfeln zurückgibt, nachdem sie geschüttelt wurden:
Typedef mit returnType definieren (.h
über @interface
-Deklaration)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Definieren Sie einen @property
für den Block (.h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Definieren Sie eine Methode mit finishBlock
(.h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Fügen Sie die zuvor definierte Methode in die .m
-Datei ein und übergeben Sie finishBlock
an den zuvor definierten @property
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Um completionBlock
auszulösen, übergeben Sie einen vordefinierten variablenTyp an ihn .__ (Vergessen Sie nicht zu überprüfen, ob completionBlock
vorhanden ist)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
Die vollständige Vorlage sieht so aus
- (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 {
}