Ja, aber Sie müssten eine Kategorie verwenden.
Etwas wie:
@interface UIControl (DDBlockActions)
- (void) addEventHandler:(void(^)(void))handler
forControlEvents:(UIControlEvents)controlEvents;
@end
Die Implementierung wäre etwas kniffliger:
#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
Einige Erklärungen:
- Wir verwenden eine benutzerdefinierte "nur interne" Klasse namens
DDBlockActionWrapper
. Dies ist eine einfache Klasse mit einer Blockeigenschaft (dem Block, den wir aufrufen möchten) und einer Methode, die diesen Block einfach aufruft.
- Die
UIControl
Kategorie instanziiert einfach einen dieser Wrapper, gibt ihm den aufzurufenden Block und fordert sich dann auf, diesen Wrapper und seine invokeBlock:
Methode als Ziel und Aktion (wie normal) zu verwenden.
- Die
UIControl
Kategorie verwendet ein zugeordnetes Objekt zum Speichern eines Arrays von DDBlockActionWrappers
, da UIControl
die Ziele nicht beibehalten werden. Dieses Array soll sicherstellen, dass die Blöcke vorhanden sind, wenn sie aufgerufen werden sollen.
Wir müssen sicherstellen , dass die DDBlockActionWrappers
get gereinigt, wenn das Objekt zerstört wird, so dass wir einen fiesen Hack von Swizzling heraus tun -[UIControl dealloc]
mit einer neuen, der das zugehörige Objekt entfernt, und ruft dann den ursprünglichen dealloc
Code. Tricky, tricky. Tatsächlich werden zugeordnete Objekte während der Freigabe automatisch bereinigt .
Schließlich wurde dieser Code im Browser eingegeben und nicht kompiliert. Es sind wahrscheinlich einige Dinge falsch daran. Ihr Kilometerstand kann variieren.
objc_implementationWithBlock()
undclass_addMethod()
lösen können als zugehörige Objekte (was eine Hash-Suche impliziert, die nicht so effizient ist wie die Methodensuche). Wahrscheinlich ein irrelevanter Leistungsunterschied, aber es ist eine Alternative.imp_implementationWithBlock
?objc_implementationWithBlock()
. :)UITableViewCell
führt zu einer Verdoppelung der gewünschten Zielaktionen, da jedes neue Ziel eine neue Instanz ist und die vorherigen nicht für dieselben Ereignisse bereinigt werden. Sie müssen zuerst die Ziele reinigenfor (id t in self.allTargets) { [self removeTarget:t action:@selector(invokeBlock:) forControlEvents:controlEvents]; } [self addTarget:target action:@selector(invokeBlock:) forControlEvents:controlEvents];
Blöcke sind Objekte. Übergeben Sie Ihren Block als
target
Argument und@selector(invoke)
alsaction
Argument wie folgt:quelle
invoke
Methode für Blockobjekte ist nicht öffentlich und soll nicht auf diese Weise verwendet werden.nil
statt@selector(invoke)
.Nein, Selektoren und Blöcke sind in Objective-C keine kompatiblen Typen (tatsächlich sind sie sehr unterschiedliche Dinge). Sie müssen Ihre eigene Methode schreiben und stattdessen deren Selektor übergeben.
quelle
Wenn Sie alle bereits bereitgestellten Antworten berücksichtigen, lautet die Antwort Ja, aber ein wenig Arbeit ist erforderlich, um einige Kategorien einzurichten.
Ich empfehle die Verwendung von NSInvocation, da Sie damit viel tun können, z. B. mit Timern, die als Objekt gespeichert und aufgerufen werden ... etc ...
Hier ist, was ich getan habe, aber beachten Sie, dass ich ARC verwende.
Erstens ist eine einfache Kategorie auf NSObject:
.h
.m
Als nächstes folgt eine Kategorie in NSInvocation, die in einem Block gespeichert werden soll:
.h
.m
So verwenden Sie es:
Mit dem Aufruf und den Standard-Objective-C-Methoden können Sie viel tun. Beispielsweise können Sie NSInvocationOperation (initWithInvocation :), NSTimer (ScheduledTimerWithTimeInterval: Aufruf: Wiederholungen :) verwenden.
Der Punkt ist, Ihren Block in eine NSInvocation zu verwandeln, ist vielseitiger und kann als solche verwendet werden:
Auch dies ist nur ein Vorschlag.
quelle
Leider nicht so einfach.
Theoretisch wäre es möglich, eine Funktion zu definieren, die der Klasse von dynamisch eine Methode hinzufügt
target
, diese Methode den Inhalt eines Blocks ausführen lässt und einen Selektor zurückgibt, der vomaction
Argument benötigt wird . Diese Funktion könnte die von MABlockClosure verwendete Technik verwenden , die im Fall von iOS von einer benutzerdefinierten Implementierung von libffi abhängt, die noch experimentell ist.Sie sollten die Aktion besser als Methode implementieren.
quelle
In der Bibliothek BlocksKit auf Github (auch als CocoaPod erhältlich) ist diese Funktion integriert.
Schauen Sie sich die Header-Datei für UIControl + BlocksKit.h an. Sie haben Dave DeLongs Idee umgesetzt, damit Sie es nicht müssen. Einige Dokumentationen finden Sie hier .
quelle
Jemand wird mir sagen, warum das vielleicht falsch ist, oder mit etwas Glück, vielleicht auch nicht, also werde ich entweder etwas lernen oder ich werde hilfreich sein.
Ich habe das einfach zusammengeschmissen. Es ist wirklich einfach, nur eine dünne Hülle mit ein bisschen Casting. Ein Wort der Warnung: Es wird davon ausgegangen, dass der von Ihnen aufgerufene Block die richtige Signatur hat, die dem von Ihnen verwendeten Selektor entspricht (dh Anzahl der Argumente und Typen).
Und
Es ist wirklich nichts Magisches los. Nur viel Downcasting
void *
und Typografie auf eine verwendbare Blocksignatur, bevor die Methode aufgerufen wird. Offensichtlich (genau wie beiperformSelector:
und der zugehörigen Methode sind die möglichen Kombinationen von Eingaben endlich, aber erweiterbar, wenn Sie den Code ändern.So verwendet:
Es gibt aus:
In einem Zielaktionsszenario müssen Sie nur Folgendes tun:
Da das Ziel in einem Zielaktionssystem nicht beibehalten wird, müssen Sie sicherstellen, dass das Aufrufobjekt so lange lebt, wie es das Steuerelement selbst tut.
Ich bin daran interessiert, etwas von jemandem zu hören, der erfahrener ist als ich.
quelle
invocation
Ich brauchte eine Aktion, die einem UIButton in einer UITableViewCell zugeordnet war. Ich wollte vermeiden, Tags zu verwenden, um jede Schaltfläche in jeder anderen Zelle aufzuspüren. Ich dachte, der direkteste Weg, dies zu erreichen, wäre, der Schaltfläche eine Block- "Aktion" wie folgt zuzuordnen:
Meine Implementierung ist etwas einfacher, dank @bbum für die Erwähnung
imp_implementationWithBlock
undclass_addMethod
(obwohl nicht ausführlich getestet):quelle
Funktioniert es nicht, eine NSBlockOperation (iOS SDK +5) zu haben? Dieser Code verwendet ARC und ist eine Vereinfachung einer App, mit der ich dies teste (scheint zumindest anscheinend zu funktionieren, nicht sicher, ob mir Speicherplatz ausgeht).
Natürlich bin ich mir nicht sicher, wie gut dies für den tatsächlichen Gebrauch ist. Sie müssen einen Verweis auf die NSBlockOperation am Leben erhalten, sonst denke ich, dass ARC sie töten wird.
quelle