Ich bin in einer Situation, in der eine Klassenmethode von meinem View-Controller aufgerufen werden muss, damit sie funktioniert, aber dann NUR einige Aktionen ausführt, nachdem die Klassenmethode abgeschlossen wurde.
(Ich denke, was ich brauche, ist ein Abschlussblock, aber bitte korrigieren Sie mich, wenn ich falsch liege.)
Hier ist die Situation:
Ich verwende Parse.com für mein Apps-Backend. Wenn sich ein Benutzer für ein Konto anmeldet, gibt er seinen Namen, seine Firma und einige andere Informationen in ein Popup ein und klickt dann auf Senden. Die Senden-Schaltfläche ist mit einer Klassenmethode (siehe unten) verknüpft, die das PFUser-Objekt und den Firmennamen verwendet und einige Datenbankobjekte erstellt. Nach Abschluss der Funktion wird das Popup mit einem Delegaten geschlossen.
Das Problem ist, dass die Erstellung dieser Objekte in einer bestimmten Reihenfolge erfolgen muss, da die Existenz der Objekt-IDs von der jeweiligen Objekt-ID abhängt. Das Problem ist, dass die Delegate-Methode zum Ausblenden des Popups sofort nach dem Klicken auf "Submit" aufgerufen wird, da dies die nächste auf dem Stapel ist.
Wenn man ein Parse-Objekt speichert, ruft man eine Methode auf, die ungefähr so aussieht:
[someParseObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// Code here runs AFTER the method completes.
// This also happens on another thread which
// I'd like to implement as well.
}];
Also, was ich brauche, um herauszufinden, wie man so etwas macht: (Alles, was mit dem Block zu tun hat, ist völlig falsch, da bin ich mir sicher)
SignUpViewController.m
myUserOrg *userOrg = [myUserOrg object]; // myUserOrg = Custom PFObject Subclass
// My method that takes in a user object and a string, creates
// the database objects in order.
[userOrg registerNewUserOrgWithUser:(PFUser*) andCompanyName:(NSString*) companyName withBlock(somethingHere)block {
if(error) {
NSLog(@"Unable to create org!");
} else {
NSLog(@"Created Org!");
[self.delegate dismissSignupView];
}
Bitte lassen Sie mich wissen, wenn Sie zusätzliche Informationen oder Erläuterungen benötigen.
Danke im Voraus!
--------- EDIT ONE ----------
Okay, also einige große Zeiteinheiten später habe ich mir das ausgedacht. Die gesamte Implementierung könnte besser vereinfacht werden und viel weniger API-Aufrufe machen, wird aber daran arbeiten. Einige andere krasse Probleme damit sind aber ein erster Schritt.
Methodenaufruf:
[testOrg registerNewUserOrgWithUser:currentUser
creatingOrgContactWithName:@"MyBigHappy Corp."
withBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(@"Not working");
} else {
NSLog(@"Working!");
}
}];
Methodenimplementierung:
@implementation MYUserOrg
@dynamic orgContact;
@dynamic orgDisplayName;
@dynamic members;
@dynamic contacts;
+ (NSString *)parseClassName {
return @"MYUserOrg";
}
dispatch_queue_t NewUserOrgRegistrationQueue;
-(void)registerNewUserOrgWithUser:(MYUser*)user
creatingOrgContactWithName:(NSString*) orgContactName
withBlock:(MYBooleanResultBlock) block {
NewUserOrgRegistrationQueue = dispatch_queue_create("com.myapp.initialOrgCreationQueue", NULL);
dispatch_async(NewUserOrgRegistrationQueue, ^{
NSMutableArray *errors = [[NSMutableArray alloc] init];
// Initial org save to generate objectId
NSError *orgSaveError = nil;
[self save:&orgSaveError];
if (orgSaveError) {
[errors addObject:@"Initial Org save Failed"];
}
// Create and Relate Org Contact
NSError *saveOrgContactError = nil;
MYontact *orgContact = [MYContact object];
[orgContact setContactType:MYContactTypeUserOrganization];
[orgContact setDisplayName:orgContactName];
[orgContact setParentOrg:self];
[orgContact save:&saveOrgContactError];
if (saveOrgContactError) {
[errors addObject:@"Saving Org Contact Failed"];
} else {
// If Org contact saved, set it;
[self setOrgContact:orgContact];
}
// Create AMD Relate User Contact
NSError *saveUserContactError = nil;
MYContact *userContact = [MYContact object];
[userContact setFirstName:user.firstName];
[userContact setLastName:user.lastName];
[userContact setContactType:MYcontactTypeUser];
[userContact setParentOrg:self];
[userContact save:&saveUserContactError];
if (saveUserContactError) {
[errors addObject:@"Saving user contact failed"];
}
NSError *saveUserError = nil;
[user setParentOrg:self];
[user setUserContact:userContact];
[user save:&saveUserError];
if (saveUserError) {
[errors addObject:@"Saving User failed"];
}
// Return if block succeeded and any errors.
NSError *error = nil;
BOOL succeeded;
if (errors.count > 0) {
NSDictionary *userInfo = @{@"error" : errors};
errors = [NSError errorWithDomain:@"MyAppErrorDomain"
code:1
userInfo:userInfo];
succeeded = NO;
} else {
succeeded = YES;
}
block(succeeded, error);
});
}
@end
Ich benutze das immer, wenn ich einen Block schreiben möchte:
Oder die weniger profane Version:
http://goshdarnblocksyntax.com/
Für Swift:
https://web.archive.org/web/20180527074325/http://fuckingswiftblocksyntax.com:80/
Ich habe einen CompletionBlock für eine Klasse geschrieben, die nach dem Schütteln die Werte eines Würfels zurückgibt:
Definiere typedef mit returnType (.h
über @interface
Erklärung)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Definiere ein @property
für den Block (.h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Definiere eine Methode mit finishBlock
(.h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Einfügen der zuvor definierten Methode in .m
Datei und Commit von finishBlock
zu @property
zuvor definiert
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Um completionBlock
auszulösen, übergeben Sie ihm einen vordefinierten Variablentyp. (Vergessen Sie nicht zu prüfen, ob completionBlock
vorhanden ist.)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
In Bezug auf http://goshdarnblocksyntax.com/
Als lokale Variable :
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
Als Eigenschaft :
@property (nonatomic, copy) returnType (^blockName)(parameterTypes);
Als Methodenparameter :
- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;
Als Argument für einen Methodenaufruf :
[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];
Als typedef :
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
Sie definieren den Block als benutzerdefinierten Typ:
typedef void (^ButtonCompletionBlock)(int buttonIndex);
Verwenden Sie es dann als Argument für eine Methode:
+ (SomeButtonView*)buttonViewWithTitle:(NSString *)title
cancelAction:(ButtonCompletionBlock)cancelBlock
completionAction:(ButtonCompletionBlock)completionBlock
Wenn Sie dies im Code aufrufen, verhält es sich wie bei jedem anderen Block:
[SomeButtonView buttonViewWithTitle:@"Title"
cancelAction:^(int buttonIndex) {
NSLog(@"User cancelled");
}
completionAction:^(int buttonIndex) {
NSLog(@"User tapped index %i", buttonIndex);
}];
Wenn es Zeit ist, den Block auszulösen, rufen Sie einfach completionBlock () auf (wobei completionBlock der Name Ihrer lokalen Kopie des Blocks ist