Come posso passare Block
a a Function
/ Method
?
Ho provato - (void)someFunc:(__Block)someBlock
senza alcun risultato.
vale a dire. Qual è il tipo per a Block
?
Come posso passare Block
a a Function
/ Method
?
Ho provato - (void)someFunc:(__Block)someBlock
senza alcun risultato.
vale a dire. Qual è il tipo per a Block
?
Risposte:
Il tipo di un blocco varia a seconda dei suoi argomenti e del suo tipo restituito. Nel caso generale, i tipi di blocco vengono dichiarati allo stesso modo dei tipi di puntatore a funzione, ma sostituendo *
con a ^
. Un modo per passare un blocco a un metodo è il seguente:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Ma come puoi vedere, è disordinato. Puoi invece usare a typedef
per rendere più puliti i tipi di blocco:
typedef void (^ IteratorBlock)(id, int);
E poi passa quel blocco a un metodo come questo:
- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
NSNumber *
o std::string&
o qualsiasi altra cosa che potresti passare come argomento di funzione. Questo è solo un esempio. (Per un blocco che è equivalente, se non per la sostituzione id
con NSNumber
la typedef
sarebbe typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
.)
NS_NOESCAPE
, ma enumerateObjectsUsingBlock
mi è stato detto che non è in fuga, ma non vedo da NS_NOESCAPE
nessuna parte nel sito, né la fuga è menzionata affatto nei documenti di Apple. Puoi aiutare?
La spiegazione più semplice per questa domanda è seguire questi modelli:
1. Blocco come parametro del metodo
Modello
- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
// your code
}
Esempio
-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
// your code
}
Altro uso di casi:
2. Blocco come proprietà
Modello
@property (nonatomic, copy) returnType (^blockName)(parameters);
Esempio
@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);
3. Blocco come argomento del metodo
Modello
[anObject aMethodWithBlock: ^returnType (parameters) {
// your code
}];
Esempio
[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
// your code
}];
4. Blocco come variabile locale
Modello
returnType (^blockName)(parameters) = ^returnType(parameters) {
// your code
};
Esempio
void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
// your code
};
5. Blocca come dattiloscritto
Modello
typedef returnType (^typeName)(parameters);
typeName blockName = ^(parameters) {
// your code
}
Esempio
typedef void(^completionBlock)(NSArray *array, NSError *error);
completionBlock didComplete = ^(NSArray *array, NSError *error){
// your code
};
Puoi fare così, passando il blocco come parametro di blocco:
//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);
Un altro modo per passare il blocco utilizzando le funzioni с nell'esempio seguente. Ho creato funzioni per eseguire qualsiasi cosa in background e nella coda principale.
file blocks.h
void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));
file 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);
}
Quindi importare blocks.h quando necessario e invocarlo:
- (void)loadInBackground {
performInBackground(^{
NSLog(@"Loading something in background");
//loading code
performOnMainQueue(^{
//completion hadler code on main queue
});
});
}
Puoi anche impostare il blocco come proprietà semplice se è applicabile per te:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
assicurarsi che la proprietà del blocco sia "copia"!
e ovviamente puoi anche usare typedef:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
Inoltre invochi o chiami un blocco usando la solita sintassi della funzione c
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Maggiori informazioni sui blocchi qui
Tendo sempre a dimenticare la sintassi dei blocchi. Mi viene sempre in mente quando devo dichiarare un blocco. Spero che aiuti qualcuno :)
Ho scritto un completamento Block per una classe che restituirà i valori dei dadi dopo che sono stati scossi:
Definire typedef con returnType ( .h
sopra la @interface
dichiarazione)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Definisci a @property
per il blocco ( .h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Definire un metodo con finishBlock
( .h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Inserisci il metodo precedentemente definito in .m
file e confermare finishBlock
con @property
definito in precedenza
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Per innescare completionBlock
passa la variabile predefinitaType ad esso (non dimenticare di verificare se completionBlock
esiste)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
Nonostante le risposte fornite su questo thread, ho davvero avuto difficoltà a scrivere una funzione che avrebbe preso un blocco come funzione - e con un parametro. Alla fine, ecco la soluzione che mi è venuta in mente.
Volevo scrivere una funzione generica loadJSONthread
, che prenderebbe l'URL di un servizio Web JSON, caricare alcuni dati JSON da questo URL su un thread in background, quindi restituire un NSArray * di risultati alla funzione chiamante.
Fondamentalmente, volevo tenere nascosta tutta la complessità del thread in background in una generica funzione riutilizzabile.
Ecco come chiamerei questa funzione:
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)
}
} ];
... e questo è il bit con cui ho lottato: come dichiararlo e come farlo chiamare la funzione Block una volta caricati i dati e passare Block
un NSArray * di record caricati:
+(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);
});
});
}
Questa domanda StackOverflow riguarda come chiamare le funzioni, passando un blocco come parametro, quindi ho semplificato il codice sopra e non ho incluso la loadJSONDataFromURL
funzione.
Ma, se sei interessato, puoi trovare una copia di questa funzione di caricamento JSON su questo blog: http://mikesknowledgebase.azurewebsite.net/pages/Services/WebServices-Page6.htm
Spero che questo aiuti alcuni altri sviluppatori XCode! (Non dimenticare di votare questa domanda e la mia risposta, se lo fa!)
Sembra il modello completo
- (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 {
}