Objective-C-Passblock als Parameter

144

Wie kann ich ein Blockan ein Function/ übergeben Method?

Ich versuchte es - (void)someFunc:(__Block)someBlockohne Erfolg.

dh. Was ist der Typ für ein Block?

Jacksonkr
quelle
7
Eine Referenz, die ich mehr benutze, als ich zugeben möchte
oltman
1
fuckblocksyntax.com :)
Aryaxt

Antworten:

256

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;
Jonathan Grynspan
quelle
Warum geben Sie ID als Argument weiter? Ist es beispielsweise nicht möglich, eine NSNumber einfach zu übergeben? Wie würde das aussehen?
Bas
7
Sie können sicherlich ein stark typisiertes Argument wie 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 idmit NSNumberder typedefwäre typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);.)
Jonathan Grynspan
Dies zeigt die Methodendeklaration. Ein Problem bei Blöcken besteht darin, dass der "chaotische" Deklarationsstil es nicht klar und einfach macht, den eigentlichen Methodenaufruf mit einem echten Blockargument zu schreiben.
Uchuugaka
Typedefs erleichtern nicht nur das Schreiben des Codes, sondern auch das Lesen erheblich, da die Block- / Funktionszeigersyntax nicht die sauberste ist.
Pyj
@JonathanGrynspan, der aus der Swift-Welt stammt, aber einen alten Objective-C-Code berühren muss, wie kann ich feststellen, ob ein Block entweicht oder nicht? Ich habe gelesen, dass Blöcke standardmäßig ausgeblendet werden, es sei denn, sie sind mit dekoriert NS_NOESCAPE, aber enumerateObjectsUsingBlockmir wurde gesagt, dass sie nicht ausgeblendet werden, aber ich sehe NS_NOESCAPEnirgendwo auf der Website etwas, und es wird in den Apple-Dokumenten überhaupt nicht erwähnt, dass es ausgeblendet wird. Kannst du helfen?
Mark A. Donohoe vor
62

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
};
EnriMR
quelle
1
[self saveWithCompletionBlock: ^ (NSArray * -Array, NSError * -Fehler) {// Ihr Code}]; In diesem Beispiel wird der Rückgabetyp ignoriert, da er ungültig ist.
Alex
51

Dies könnte hilfreich sein:

- (void)someFunc:(void(^)(void))someBlock;
Quaertym
quelle
Sie vermissen eine Klammer
Newacct
Dieser hat für mich gearbeitet, der vorherige nicht. Übrigens danke Kumpel, das war in der Tat hilfreich!
Tanou
23

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);
Aleksei Minaev
quelle
8

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
        });
    });
}
Dren
quelle
6

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;
iiFreeman
quelle
4

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

http://fuckingblocksyntax.com

Juan Sagasti
quelle
Das hat mir Zeit gespart
Le Ding
3

Ich habe einen CompletionBlock für eine Klasse geschrieben, die die Werte der Würfel zurückgibt, nachdem sie geschüttelt wurden:

  1. Definieren Sie typedef mit returnType ( .hobige @interfaceDeklaration)

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
  2. Definiere a @propertyfür den Block ( .h)

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
  3. Definieren Sie eine Methode mit finishBlock( .h)

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
  4. Insert vorherige definierte Methode in .mDatei und verpflichten finishBlockzu @propertydefiniert vor

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
  5. Um completionBlockeinen vordefinierten variablen Typ an ihn auszulösen (Vergessen Sie nicht zu prüfen, ob der completionBlockvorhanden ist)

    if( self.completeDiceRolling ){
        self.completeDiceRolling(self.dieValue);
    }
Alex Cio
quelle
2

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 Blockbringt, 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 loadJSONDataFromURLFunktion 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!)

Mike Gledhill
quelle
1
Dies ist einer der besten Tricks, die ich für iOS und Blocks gesehen habe. Ich liebe es, Mann !!!!
Portforwardpodcast
1

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 {

}
yoAlex5
quelle