2012-04-07 2 views
0

Ordnung erstellt wird ist, was ich versuche zu tun: ein ModellobjektErstellen Sie ein Objekt und haben es warten, bis es hier

Ich habe das von einigen Netzwerkdaten ausgefüllt ist, möchte ich es schaffen können, wie dies:

// createWithID will fire off a request to a server 
MyObject *newObject = [MyObject createWithID:123]; 
if(newObject){ 
    //do something! 

} 

das Problem, das ich habe, ist der Netzwerk-Aufruf innerhalb der createWithID Methode asynchron ist, so dass das Verfahren wird immer wieder zurückkehren, bevor der Anruf beendet ist.

Erstens, ist dies eine gute Möglichkeit, dies zu tun? Ich mag die Idee, den Netzwerkaufruf in der Methode einzukapseln. Zweitens, kann es getan werden, ohne den Hauptfaden zu blockieren?

Danke!

+0

Warte, hast du gerade gesagt, dass du *** den Haupt Thread blockieren willst?!? –

+0

Ich stimme zu, Blockieren der ** MAIN ** Thread staut Ihre App ... – Sirens

+1

Es wäre besser, einen neuen Thread zu erstellen, dann fügen Sie einen UIActivityIndictor oder etwas, um zu zeigen, dass der Netzwerkanruf in Bearbeitung ist. – Otium

Antwort

3

auf der @ danh ein bisschen Antwort zu erweitern, würde ich sagen, dass Sie nicht bis einen Zeiger auf das Objekt haben soll, nachdem es fertig ist. Zum Beispiel:

[MyObject makeObjectWithID:123 completion:^(MyObject *object, NSError *error) { 
    if (object == nil) { 
    NSLog(@"error creating object: %@", error); 
    } else { 
    NSLog(@"created object: %@", object); 
    } 
}]; 

Und dann wäre die Schaffung Methode so etwas wie dieses:

+ (void)makeObjectWithID:(NSInteger)objectID completion:(void(^)(MyObject*,NSError*))handler { 
    handler = Block_copy(handler); 
    dispatch_async(dispatch_get_global_queue(0,0), ^{ 
    MyObject *object = [[MyObject alloc] initWithID:objectID]; 

    NSError *error = nil; 
    BOOL succeeded = [object doTheExpensiveAndBlockingSetupThingWithError:&error]; 

    if (succeeded == NO) { 
     [object release], object = nil; 
    } 

    dispatch_async(dispatch_get_main_queue(), ^{ 
     handler([object autorelease], error); 
    }); 
    }); 
    Block_release(handler); 
} 

Grundsätzlich finde ich, dass die sicherere Sie die API machen, desto weniger wahrscheinlich werden Sie schrauben etwas hoch. Dieser Weg ist sicher, weil Sie nie einen Zeiger auf einen teilweise konstruierten MyObject haben. Es gibt keine Möglichkeit, einen zu bekommen und es nicht bereit sein zu lassen. Zugegeben, es gibt keinen Aufhebungsmechanismus, aber Sie können dies immer noch erweitern, um das zu ermöglichen.

2

Wenn ich Sie verstehe, führt MyObject eine asynch-Anfrage aus, um die Einrichtung zu beenden, so dass der Aufrufer nicht davon ausgehen kann, dass die erstellte Instanz bereit ist, bis die asynch-Einrichtung abgeschlossen ist.

Der Weg, dies zu lösen, besteht darin, den Anrufer mit dem Wissen zu konfrontieren, dass das Setup durchgeführt wurde. Es gibt ein paar gute Ansätze, zum Beispiel den Anrufer zu einem Delegierten von MyObject zu machen oder MyObject eine NSNotifikation zu senden, wenn es fertig ist, aber ich bevorzuge Blöcke, weil ich finde, dass der Code des Anrufers oft leichter zu lesen ist.

Die Art und Weise zu tun, ist so ... in MyObject.h:

typedef void (^CompletionBlock)(id result, NSError *error); 

+ (MyObject *)createWithId:(NSInteger)anId completion:(CompletionBlock)completion; 

Mein Objekt den Block als Ivar halten kann, wenn er muss (mit einer @property (Kopie ...)), dann rufen Sie es auf, wenn das Setup abgeschlossen ist.

Nun wird der Anrufer wie folgt aussehen:

// do something to indicate activity, like show an activity indicator 
MyObject *newObject = [MyObject createWithID:123 completion::^(id r, NSError *e) { 
    // hide the activity indicator 
    if (!error) { 
     // code here gets executed when newObject is ready... update the ui accordingly 
    } 
}]; 

// code here gets executed right away, and should not assume that newObject is ready 
Verwandte Themen