Blocco pass Objective-C come parametro


Risposte:


257

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 typedefper 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;

Perché stai passando ID come argomento? Ad esempio, non è possibile passare facilmente un numero NSN? Come sarebbe?
bas

7
Puoi certamente passare un argomento fortemente tipizzato come 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 idcon NSNumberla typedefsarebbe typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);.)
Jonathan Grynspan

Questo mostra la dichiarazione del metodo. Un problema con i blocchi è che lo stile di dichiarazione "disordinato" non rende chiaro e facile scrivere la chiamata del metodo reale con un argomento di blocco reale.
Uchuugaka,

Typedefs non solo rende il codice più facile da scrivere, ma significativamente più facile da leggere poiché la sintassi del puntatore blocco / funzione non è la più pulita.
Pyj,

@JonathanGrynspan, proveniente dal mondo Swift ma dovendo toccare un vecchio codice Objective-C, come posso sapere se un blocco sta scappando o no? Ho letto che per impostazione predefinita, i blocchi stanno scappando, tranne se decorato con NS_NOESCAPE, ma enumerateObjectsUsingBlockmi è stato detto che non è in fuga, ma non vedo da NS_NOESCAPEnessuna parte nel sito, né la fuga è menzionata affatto nei documenti di Apple. Puoi aiutare?
Mark A. Donohoe,

62

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
};

1
[self saveWithCompletionBlock: ^ (array NSArray *, errore NSError *) {// your code}]; In questo esempio il tipo restituito viene ignorato perché è nullo?
Alex

51

Questo potrebbe essere utile:

- (void)someFunc:(void(^)(void))someBlock;

ti manca una parentesi
Newacct

Questo ha funzionato per me mentre il precedente no. A proposito grazie amico, è stato davvero utile!
Tanou,

23

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);

8

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
        });
    });
}

6

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;



3

Ho scritto un completamento Block per una classe che restituirà i valori dei dadi dopo che sono stati scossi:

  1. Definire typedef con returnType ( .hsopra la @interfacedichiarazione)

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
  2. Definisci a @propertyper il blocco ( .h)

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
  3. Definire un metodo con finishBlock ( .h)

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
  4. Inserisci il metodo precedentemente definito in .m file e confermare finishBlockcon @propertydefinito in precedenza

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
  5. Per innescare completionBlockpassa la variabile predefinitaType ad esso (non dimenticare di verificare se completionBlockesiste)

    if( self.completeDiceRolling ){
        self.completeDiceRolling(self.dieValue);
    }

2

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 Blockun 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 loadJSONDataFromURLfunzione.

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!)


1
Questo è davvero uno dei migliori trucchi che ho visto per iOS e blocchi. Lo adoro amico !!!!
portforwardpodcast

1

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 {

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.