Wie kann ich ein Block
an ein Function
/ übergeben Method
?
Ich versuchte es - (void)someFunc:(__Block)someBlock
ohne Erfolg.
dh. Was ist der Typ für ein Block
?
Wie kann ich ein Block
an ein Function
/ übergeben Method
?
Ich versuchte es - (void)someFunc:(__Block)someBlock
ohne Erfolg.
dh. Was ist der Typ für ein Block
?
Antworten:
Der Typ eines Blocks hängt von seinen Argumenten und seinem Rückgabetyp ab. Im allgemeinen Fall werden Blocktypen genauso deklariert wie Funktionszeigertypen, jedoch durch *
a ersetzt ^
. Eine Möglichkeit, einen Block an eine Methode zu übergeben, ist folgende:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Aber wie Sie sehen können, ist das chaotisch. Sie können stattdessen a verwenden typedef
, 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;
NSNumber *
oder std::string&
oder irgendetwas anderes übergeben, das Sie als Funktionsargument übergeben könnten. Dies ist nur ein Beispiel. (Für einen Block, der mit Ausnahme des Ersatzes gleichwertig ist id
mit NSNumber
der typedef
wäre typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
.)
NS_NOESCAPE
, aber enumerateObjectsUsingBlock
mir wurde gesagt, dass sie nicht ausgeblendet werden, aber ich sehe NS_NOESCAPE
nirgendwo auf der Website etwas, und es wird in den Apple-Dokumenten überhaupt nicht erwähnt, dass es ausgeblendet wird. Kannst du helfen?
Die einfachste Erklärung für diese Frage ist die Befolgung dieser Vorlagen:
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 Sie 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. Als typedef blockieren
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 tun, indem Sie den 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, den Block mit den Funktionen с im folgenden Beispiel zu übergeben. Ich habe Funktionen erstellt, um alles im Hintergrund und in der Hauptwarteschlange auszuführen.
blocks.h Datei
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);
}
Als import blocks.h wenn nötig und rufen Sie 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 gilt:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
Stellen Sie sicher, dass die Blockeigenschaft "copy" ist!
und natürlich können Sie auch typedef verwenden:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
Außerdem rufen Sie einen Block mit der üblichen c-Funktionssyntax auf oder rufen ihn auf
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Mehr Infos zu Blöcken hier
Ich neige immer dazu, die Blocksyntax zu vergessen. Das fällt mir immer ein, wenn ich einen Block deklarieren muss. Ich hoffe es hilft jemandem :)
Ich habe einen CompletionBlock für eine Klasse geschrieben, die die Werte der Würfel zurückgibt, nachdem sie geschüttelt wurden:
Definieren Sie typedef mit returnType ( .h
obige @interface
Deklaration)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Definiere a @property
für den Block ( .h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Definieren Sie eine Methode mit finishBlock
( .h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Insert vorherige definierte Methode in .m
Datei und verpflichten finishBlock
zu @property
definiert vor
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Um completionBlock
einen vordefinierten variablen Typ an ihn auszulösen (Vergessen Sie nicht zu prüfen, ob der completionBlock
vorhanden ist)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
Trotz der Antworten in diesem Thread hatte ich wirklich Mühe, eine Funktion zu schreiben, die einen Block als Funktion verwendet - und zwar mit einem Parameter. Hier ist schließlich die Lösung, die ich gefunden habe.
Ich wollte eine generische Funktion schreiben loadJSONthread
, die die URL eines JSON-Webdienstes verwendet, einige JSON-Daten von dieser URL in einen Hintergrundthread lädt und dann ein NSArray * der Ergebnisse an die aufrufende Funktion zurückgibt.
Grundsätzlich wollte ich die gesamte Komplexität des Hintergrund-Threads in einer generischen wiederverwendbaren Funktion verbergen.
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 das ist das Bit, mit dem ich zu kämpfen hatte: wie man es deklariert und wie man es dazu Block
bringt, die Block-Funktion aufzurufen, sobald die Daten geladen wurden, und das NSArray * der geladenen Datensätze zu übergeben:
+(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 betrifft das Aufrufen von Funktionen, wobei ein Block als Parameter übergeben wird. Daher habe ich den obigen Code vereinfacht und die loadJSONDataFromURL
Funktion nicht eingeschlossen .
Wenn Sie interessiert sind, finden Sie 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, wenn dies der Fall ist!)
Die vollständige Vorlage sieht aus wie
- (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 {
}