ios - Cómo escribir un bloque de finalización de Objective-C
block in objective c (4)
En cuanto a http://goshdarnblocksyntax.com/
Como una variable local :
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
Como una propiedad :
@property (nonatomic, copy) returnType (^blockName)(parameterTypes);
Como un parámetro de método :
- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;
Como argumento para una llamada a un método :
[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];
Como un typedef :
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
Estoy en una situación en la que necesito llamar a un método de clase desde mi controlador de vista, hacer lo propio, pero luego realizar algunas acciones SÓLO DESPUÉS de que el método de clase se haya completado.
(Creo que lo que necesito es un bloque de finalización, pero por favor corrígeme si me equivoco).
Aquí está la situación:
Estoy usando Parse.com para mis aplicaciones back-end. Cuando un usuario se registra en una cuenta, ingresa su nombre, compañía y alguna otra información en una ventana emergente y luego hace clic en enviar. El botón de enviar está vinculado a un método de clase (que se muestra a continuación) que toma su objeto PFUser y el nombre de la compañía y crea algunos objetos de base de datos. Una vez que la función se completa, la ventana emergente se cierra con un delegado.
El problema es que necesito que la creación de estos objetos ocurra en un orden específico porque dependen de que los demás objetos existan. El problema es que el método de delegado para cerrar la ventana emergente se llama inmediatamente después de hacer clic en enviar ya que es el siguiente en la pila.
Al guardar un objeto Parse uno llama a un método que se ve así: (Esto es lo que espero escribir y creo que resolvería mi problema)
[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.
}];
Por lo tanto, lo que necesito saber es cómo hacer algo como lo siguiente: (Todo lo que tiene que ver con el bloque es completamente erróneo, estoy seguro)
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];
}
Por favor, avíseme si necesita información adicional o aclaraciones.
¡Gracias de antemano!
--------- EDIT ONE ----------
Muy bien, así que varias unidades de tiempo importantes más tarde, esto es lo que se me ocurrió. Toda la implementación podría simplificarse mejor y hacer muchas menos llamadas de API, pero trabajará en eso. Varios otros problemas evidentes también, pero es un primer paso.
Método de llamada:
[testOrg registerNewUserOrgWithUser:currentUser
creatingOrgContactWithName:@"MyBigHappy Corp."
withBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(@"Not working");
} else {
NSLog(@"Working!");
}
}];
Implementación del método:
@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
Escribí un completionBlock para una clase que devolverá los valores de un dado después de que hayan sido sacudidos:
Definir typedef con returnType (
.h
arriba@interface
declaración de@interface
)typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Defina una
@property
para el bloque (.h
)@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Definir un método con
finishBlock
(.h
)- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Inserta el método definido anteriormente en el archivo
.m
y confirmafinishBlock
a@property
definido antes- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{ self.completeDiceRolling = finishBlock; }
Para desencadenar la
completionBlock
pase el tipo de variable predefinido (No olvide comprobar si existe el bloqueo decompletionBlock
)if( self.completeDiceRolling ){ self.completeDiceRolling(self.dieValue); }
Siempre uso esto cuando quiero escribir un bloque:
EDITAR
Si escribe Swift, use esto:
http://fuckingswiftblocksyntax.com
Si los enlaces anteriores no abren debido al lenguaje obsceno, utilice este.
Usted define el bloque como un tipo personalizado:
typedef void (^ButtonCompletionBlock)(int buttonIndex);
Luego utilícelo como argumento para un método:
+ (SomeButtonView*)buttonViewWithTitle:(NSString *)title
cancelAction:(ButtonCompletionBlock)cancelBlock
completionAction:(ButtonCompletionBlock)completionBlock
Cuando llamas esto en código, es como cualquier otro bloque:
[SomeButtonView buttonViewWithTitle:@"Title"
cancelAction:^(int buttonIndex) {
NSLog(@"User cancelled");
}
completionAction:^(int buttonIndex) {
NSLog(@"User tapped index %i", buttonIndex);
}];
Si llega el momento de activar el bloqueo, simplemente llame a completionBlock () (donde completionBlock es el nombre de su copia local del bloque)