Como posso passar um Block
para um Function
/ Method
?
Eu tentei - (void)someFunc:(__Block)someBlock
sem sucesso.
ie Qual é o tipo de a Block
?
Como posso passar um Block
para um Function
/ Method
?
Eu tentei - (void)someFunc:(__Block)someBlock
sem sucesso.
ie Qual é o tipo de a Block
?
Respostas:
O tipo de um bloco varia de acordo com seus argumentos e seu tipo de retorno. No caso geral, os tipos de bloco são declarados da mesma maneira que os tipos de ponteiros de função, mas substituindo o *
por a ^
. Uma maneira de passar um bloco para um método é a seguinte:
- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;
Mas como você pode ver, isso é confuso. Você pode usar um typedef
para tornar os tipos de bloco mais limpos:
typedef void (^ IteratorBlock)(id, int);
E então passe esse bloco para um método como este:
- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
NSNumber *
ou std::string&
ou qualquer outra coisa que você poderia passar como argumento de função. Este é apenas um exemplo. (Para um bloco que é equivalente, exceto para a substituição id
com NSNumber
o typedef
seria typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);
.)
NS_NOESCAPE
, mas enumerateObjectsUsingBlock
me disseram que não é escapável, mas não vejo NS_NOESCAPE
nenhum lugar no site, nem o escape mencionado nos documentos da Apple. Você pode ajudar?
A explicação mais fácil para esta pergunta é seguir estes modelos:
1. Bloquear como parâmetro de método
Modelo
- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
// your code
}
Exemplo
-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
// your code
}
Outro uso de casos:
2. Bloquear como uma propriedade
Modelo
@property (nonatomic, copy) returnType (^blockName)(parameters);
Exemplo
@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);
3. Bloquear como argumento de método
Modelo
[anObject aMethodWithBlock: ^returnType (parameters) {
// your code
}];
Exemplo
[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
// your code
}];
4. Bloquear como uma variável local
Modelo
returnType (^blockName)(parameters) = ^returnType(parameters) {
// your code
};
Exemplo
void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
// your code
};
5. Bloquear como um typedef
Modelo
typedef returnType (^typeName)(parameters);
typeName blockName = ^(parameters) {
// your code
}
Exemplo
typedef void(^completionBlock)(NSArray *array, NSError *error);
completionBlock didComplete = ^(NSArray *array, NSError *error){
// your code
};
Isso pode ser útil:
- (void)someFunc:(void(^)(void))someBlock;
Você pode fazer assim, passando o bloco como um parâmetro do bloco:
//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);
Mais uma maneira de passar o bloco usando as funções ñ no exemplo abaixo. Eu criei funções para executar qualquer coisa em segundo plano e na fila principal.
arquivo blocks.h
void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));
arquivo 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);
}
Em seguida, importe blocks.h quando necessário e chame-o:
- (void)loadInBackground {
performInBackground(^{
NSLog(@"Loading something in background");
//loading code
performOnMainQueue(^{
//completion hadler code on main queue
});
});
}
Você também pode definir o bloco como uma propriedade simples, se aplicável:
@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);
verifique se a propriedade do bloco é "cópia"!
e é claro que você também pode usar o typedef:
typedef void (^SimpleBlock)(id);
@property (nonatomic, copy) SimpleBlock someActionHandler;
Além disso, você invoca ou chama um bloco usando a sintaxe usual da função c
-(void)iterateWidgets:(IteratorBlock)iteratorBlock{
iteratorBlock(someId, someInt);
}
Mais informações sobre blocos aqui
Costumo sempre esquecer a sintaxe dos blocos. Isso sempre vem à minha mente quando preciso declarar um bloqueio. Eu espero que isso ajude alguém :)
Escrevi um completeBlock para uma classe que retornará os valores dos dados depois que eles foram sacudidos:
Defina typedef com returnType ( declaração .h
acima @interface
)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Defina a @property
para o bloco ( .h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Defina um método com finishBlock
( .h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Insira o método definido anterior no .m
arquivo e confirme finishBlock
com o @property
definido antes
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
Para acionar a completionBlock
passagem de typeType predefinido (não se esqueça de verificar se completionBlock
existe)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
Apesar das respostas dadas neste tópico, eu realmente lutei para escrever uma função que levaria um bloco como uma função - e com um parâmetro. Eventualmente, aqui está a solução que eu encontrei.
Eu queria escrever uma função genérica loadJSONthread
, que pegasse a URL de um serviço da Web JSON, carregasse alguns dados JSON dessa URL em um encadeamento em segundo plano e retornasse um NSArray * dos resultados de volta à função de chamada.
Basicamente, eu queria manter toda a complexidade do thread em segundo plano escondida em uma função reutilizável genérica.
Aqui está como eu chamaria essa função:
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)
}
} ];
... e foi com isso que lutei: como declará-lo e como fazê-lo chamar a função Block depois que os dados foram carregados e passar o Block
NSArray * dos registros carregados:
+(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 questão StackOverflow se refere a como chamar funções, passando um bloco como parâmetro, então simplifiquei o código acima e não incluí a loadJSONDataFromURL
função.
Mas, se você estiver interessado, poderá encontrar uma cópia dessa função de carregamento JSON neste blog: http://mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm
Espero que isso ajude outros desenvolvedores de XCode! (Não esqueça de votar nesta pergunta e na minha resposta, se houver!)
O modelo completo parece
- (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 {
}