Sim, mas você teria que usar uma categoria.
Algo como:
@interface UIControl (DDBlockActions)
- (void) addEventHandler:(void(^)(void))handler
forControlEvents:(UIControlEvents)controlEvents;
@end
A implementação seria um pouco mais complicada:
#import <objc/runtime.h>
@interface DDBlockActionWrapper : NSObject
@property (nonatomic, copy) void (^blockAction)(void);
- (void) invokeBlock:(id)sender;
@end
@implementation DDBlockActionWrapper
@synthesize blockAction;
- (void) dealloc {
[self setBlockAction:nil];
[super dealloc];
}
- (void) invokeBlock:(id)sender {
[self blockAction]();
}
@end
@implementation UIControl (DDBlockActions)
static const char * UIControlDDBlockActions = "unique";
- (void) addEventHandler:(void(^)(void))handler
forControlEvents:(UIControlEvents)controlEvents {
NSMutableArray * blockActions =
objc_getAssociatedObject(self, &UIControlDDBlockActions);
if (blockActions == nil) {
blockActions = [NSMutableArray array];
objc_setAssociatedObject(self, &UIControlDDBlockActions,
blockActions, OBJC_ASSOCIATION_RETAIN);
}
DDBlockActionWrapper * target = [[DDBlockActionWrapper alloc] init];
[target setBlockAction:handler];
[blockActions addObject:target];
[self addTarget:target action:@selector(invokeBlock:) forControlEvents:controlEvents];
[target release];
}
@end
Alguma explicação:
- Estamos usando uma classe personalizada "apenas interna" chamada
DDBlockActionWrapper
. Esta é uma classe simples que possui uma propriedade de bloco (o bloco que queremos invocar) e um método que simplesmente invoca esse bloco.
- A
UIControl
categoria simplesmente instancia um desses invólucros, fornece a ele o bloco a ser invocado e, a seguir, diz a si mesma para usar esse invólucro e seu invokeBlock:
método como destino e ação (como normal).
- A
UIControl
categoria usa um objeto associado para armazenar um array de DDBlockActionWrappers
, porque UIControl
não retém seus alvos. Essa matriz é para garantir que os blocos existam quando deveriam ser invocados.
Temos que garantir que DDBlockActionWrappers
sejam limpos quando o objeto for destruído, então estamos fazendo um hack desagradável de swizzling -[UIControl dealloc]
com um novo que remove o objeto associado e, em seguida, invoca o dealloc
código original . Complicado, complicado. Na verdade, os objetos associados são limpos automaticamente durante a desalocação .
Finalmente, este código foi digitado no navegador e não foi compilado. Provavelmente há algumas coisas erradas com ele. Sua milhagem pode variar.
objc_implementationWithBlock()
eclass_addMethod()
para resolver esse problema de uma maneira um pouco mais eficiente do que usar objetos associados (o que implica em uma pesquisa de hash que não é tão eficiente quanto a pesquisa de método). Provavelmente uma diferença de desempenho irrelevante, mas é uma alternativa.imp_implementationWithBlock
?objc_implementationWithBlock()
. :)UITableViewCell
's resultará na duplicação de alvos-ações desejados, já que cada novo alvo é uma nova instância e os anteriores não são limpos para os mesmos eventos. Você tem que limpar os alvos primeirofor (id t in self.allTargets) { [self removeTarget:t action:@selector(invokeBlock:) forControlEvents:controlEvents]; } [self addTarget:target action:@selector(invokeBlock:) forControlEvents:controlEvents];
Os blocos são objetos. Passe seu bloco como o
target
argumento, com@selector(invoke)
como oaction
argumento, assim:fonte
invoke
método em objetos Block não é público e não se destina a ser usado dessa maneira.nil
vez de@selector(invoke)
.Não, seletores e blocos não são tipos compatíveis em Objective-C (na verdade, eles são coisas muito diferentes). Você terá que escrever seu próprio método e passar seu seletor.
fonte
Considerando todas as respostas já fornecidas, a resposta é Sim, mas um pouco de trabalho é necessário para configurar algumas categorias.
Eu recomendo usar NSInvocation porque você pode fazer muito com isso, como temporizadores, armazenados como um objeto e invocados ... etc ...
Aqui está o que eu fiz, mas observe que estou usando o ARC.
A primeira é uma categoria simples no NSObject:
.h
.m
A seguir está uma categoria em NSInvocation para armazenar em um bloco:
.h
.m
Aqui está como usá-lo:
Você pode fazer muito com a invocação e os métodos Objective-C padrão. Por exemplo, você pode usar NSInvocationOperation (initWithInvocation :), NSTimer (storedTimerWithTimeInterval: invocation: repeates :)
O ponto é transformar seu bloco em um NS. O Invocation é mais versátil e pode ser usado como tal:
Novamente, esta é apenas uma sugestão.
fonte
Não é tão simples assim, infelizmente.
Em teoria, seria possível definir uma função que adiciona dinamicamente um método à classe de
target
, fazer com que esse método execute o conteúdo de um bloco e retornar um seletor conforme necessário para oaction
argumento. Esta função poderia utilizar a técnica utilizada pelo MABlockClosure , que, no caso do iOS, depende de uma implementação customizada de libffi, que ainda é experimental.É melhor você implementar a ação como um método.
fonte
A biblioteca BlocksKit no Github (também disponível como CocoaPod) possui esse recurso integrado.
Dê uma olhada no arquivo de cabeçalho de UIControl + BlocksKit.h. Eles implementaram a ideia de Dave DeLong para que você não precise fazer isso. Alguma documentação está aqui .
fonte
Alguém vai me dizer por que isso está errado, talvez, ou com alguma sorte, talvez não, então ou aprenderei algo ou serei útil.
Eu apenas juntei isso. É realmente básico, apenas uma embalagem fina com um pouco de fundição. Uma palavra de advertência, ele assume que o bloco que você está invocando tem a assinatura correta para corresponder ao seletor que você usa (isto é, número de argumentos e tipos).
E
Não há realmente nada mágico acontecendo. Apenas muito downcasting
void *
e typecasting para uma assinatura de bloco utilizável antes de invocar o método. Obviamente (assim como comperformSelector:
e método associado, as combinações possíveis de entradas são finitas, mas extensíveis se você modificar o código.Usado assim:
Ele produz:
Usado em um cenário de ação-alvo, você só precisa fazer algo assim:
Como o alvo em um sistema de ação de destino não é retido, você precisará garantir que o objeto de invocação viva enquanto o próprio controle durar.
Estou interessado em ouvir qualquer coisa de alguém mais especialista do que eu.
fonte
invocation
nunca foi lançadoEu precisava ter uma ação associada a um UIButton dentro de um UITableViewCell. Eu queria evitar o uso de tags para rastrear cada botão em cada célula diferente. Achei que a maneira mais direta de conseguir isso era associar uma "ação" de bloqueio ao botão, assim:
Minha implementação é um pouco mais simplificada, graças a @bbum por mencionar
imp_implementationWithBlock
eclass_addMethod
, (embora não tenha sido exaustivamente testado):fonte
Não funciona ter um NSBlockOperation (iOS SDK +5). Este código usa ARC e é uma simplificação de um aplicativo com o qual estou testando (parece funcionar, pelo menos aparentemente, não tenho certeza se estou perdendo memória).
Claro, não tenho certeza de como isso é bom para uso real. Você precisa manter uma referência ao NSBlockOperation viva ou acho que o ARC irá eliminá-lo.
fonte