Objective-C-Passblock als Parameter


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;

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

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;

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

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

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;



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

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


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 {

}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.