¿Cómo puedo pasar de Block
a a Function
/ Method
?
Lo intenté - (void)someFunc:(__Block)someBlock
sin resultado.
es decir. ¿Cuál es el tipo de a Block
?
¿Cómo puedo pasar de Block
a a Function
/ Method
?
Lo intenté - (void)someFunc:(__Block)someBlock
sin resultado.
es decir. ¿Cuál es el tipo de a Block
?
Respuestas:
El tipo de un bloque varía según sus argumentos y su tipo de retorno. En el caso general, los tipos de bloque se declaran de la misma manera que los tipos de puntero de función, pero reemplazando el *
con a ^
. Una forma de pasar un bloque a un método es la siguiente:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Pero como puedes ver, eso es desordenado. En su lugar, puede usar a typedef
para hacer que los tipos de bloques sean más limpios:
typedef void (^ IteratorBlock)(id, int);
Y luego pasa ese bloque a un método como este:
- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
NSNumber *
o std::string&
, o cualquier otra cosa que podría pasar como un argumento de función. Esto es solo un ejemplo. (Para un bloque que es equivalente a excepción de la sustitución id
con NSNumber
, la typedef
habría typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
.)
NS_NOESCAPE
, pero enumerateObjectsUsingBlock
me dicen que no escapan, pero no veo NS_NOESCAPE
ningún lugar en el sitio, ni se menciona el escape en absoluto en los documentos de Apple. ¿Puede usted ayudar?
La explicación más fácil para esta pregunta es seguir estas plantillas:
1. Bloquear como parámetro de método
Modelo
- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
// your code
}
Ejemplo
-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
// your code
}
Otro uso de casos:
2. Bloquear como una propiedad
Modelo
@property (nonatomic, copy) returnType (^blockName)(parameters);
Ejemplo
@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);
3. Bloquear como argumento de método
Modelo
[anObject aMethodWithBlock: ^returnType (parameters) {
// your code
}];
Ejemplo
[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
// your code
}];
4. Bloquear como una variable local
Modelo
returnType (^blockName)(parameters) = ^returnType(parameters) {
// your code
};
Ejemplo
void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
// your code
};
5. Bloquear como typedef
Modelo
typedef returnType (^typeName)(parameters);
typeName blockName = ^(parameters) {
// your code
}
Ejemplo
typedef void(^completionBlock)(NSArray *array, NSError *error);
completionBlock didComplete = ^(NSArray *array, NSError *error){
// your code
};
Puede hacer esto, pasando el bloque como un parámetro de bloque:
//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);
Una forma más de pasar bloque usando las funciones с en el ejemplo a continuación. He creado funciones para realizar cualquier cosa en segundo plano y en la cola principal.
archivo blocks.h
void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));
archivo blocks.m
#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);
}
Luego importe import blocks.h cuando sea necesario e invoque:
- (void)loadInBackground {
performInBackground(^{
NSLog(@"Loading something in background");
//loading code
performOnMainQueue(^{
//completion hadler code on main queue
});
});
}
También puede establecer el bloqueo como una propiedad simple si es aplicable para usted:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
¡asegúrese de que la propiedad de bloque sea "copiar"!
y, por supuesto, también puedes usar typedef:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
También invocas o llamas a un bloque usando la sintaxis habitual de la función c
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Más información sobre bloques aquí
Siempre tiendo a olvidarme de la sintaxis de bloques. Esto siempre viene a mi mente cuando necesito declarar un bloqueo. Espero que esto ayude a alguien :)
Escribí un Bloque de finalización para una clase que devolverá los valores de los dados después de que hayan sido sacudidos:
Definir typedef con returnType ( declaración .h
anterior @interface
)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Definir a @property
para el bloque ( .h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Definir un método con finishBlock
( .h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Inserte el método definido anterior en el .m
archivo y comprométase finishBlock
a @property
definido antes
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Para desencadenar completionBlock
pasar variableType predefinida (no olvide comprobar si completionBlock
existe)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
A pesar de las respuestas dadas en este hilo, realmente tuve problemas para escribir una función que tomara un Bloque como función, y con un parámetro. Finalmente, esta es la solución que se me ocurrió.
Quería escribir una función genérica loadJSONthread
, que tomaría la URL de un servicio web JSON, cargaría algunos datos JSON de esta URL en un hilo de fondo y luego devolvería un NSArray * de resultados a la función de llamada.
Básicamente, quería mantener toda la complejidad del hilo de fondo oculta en una función genérica reutilizable.
Así es como llamaría a esta función:
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)
}
} ];
... y este es el bit con el que luché: cómo declararlo y cómo hacer que llame a la función Bloquear una vez que se cargaron los datos, y pasar Block
un NSArray * de registros cargados:
+(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);
});
});
}
Esta pregunta de StackOverflow se refiere a cómo llamar a funciones, pasando un Bloque como parámetro, por lo que he simplificado el código anterior y no he incluido la loadJSONDataFromURL
función.
Pero, si está interesado, puede encontrar una copia de esta función de carga JSON en este blog: http://mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm
¡Espero que esto ayude a otros desarrolladores de XCode! (¡No olvide votar esta pregunta y mi respuesta, si es así!)
La plantilla completa se ve como
- (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 {
}