2013-06-14 17 views
6

Ich möchte ein kleines Plugin-System für Objekte in objective-c erstellen.Objective-C Funktionalität zu jeder Methode eines Objekts hinzufügen

Jetzt bin ich an der Stelle fest, wo ich dynamisch will (zur Laufzeit) zu jeder Funktion in einem Objekt eine Codezeile hinzuzufügen.

Ich habe mit der Laufzeitbibliothek herum gespielt, kam aber zu keiner Lösung, noch.

Was ich bisher versucht haben, ist dies:

 id (^impyBlock)(id, id, ...) = ^(id self_, id arguments, ...) 
     { 
      // My custom code for every function here 
      id (*func)(__strong id,SEL,...) = (id (*)(__strong id, SEL, ...))imp; 
      return func(obj, s, arguments); 

     }; 

     id (*impyFunct)(id, SEL,...) = imp_implementationWithBlock(impyBlock); 
     method_setImplementation(mList[i], impyFunct); 

Mein Problem ist, wenn es mehr als ein Argument ist, ich habe keine Chance, sie zu übergeben „func()“. AFAIK ist das nicht möglich in C.

Eine andere Lösung, über die ich nachgedacht habe, ist etwas Magie mit Methodenwechsel.

In den Schritten:

  1. erstellen „swizzle-Methode“, die gerade ruft meine eigenen Code und ruft die ursprüngliche Methode danach (durch ein Namensschema)
  2. Ändern der IMP jeder Funktion mit dem von die „swizzle-Methode“
  3. erstellen Ihnen eine neue Methode mit der „alten“ Implementierung und wie „___name“ zu nennen

In dieser Lösung i am Punkt steckte 3. ich habe keinen Mana ein Schema ändern ged, um dynamisch eine vollständige neue Methode zu erstellen.

Hat jemand mich mit meinen Problemen oben helfen können, oder hat eine andere Lösung für eine „alle Methode Funktionalität fangen“.

Das beste wäre etwas wie forwardInvocation, das auch bereits definierte Funktionen fängt.

Danke für Ihre Hilfe!

+2

Dies ist eine ziemlich intensive schwarze Magie. Bist du sicher, dass du das tun musst? Ich liebe es, selbst mit solchen Sachen herumzuspielen und sie zu unterstützen, aber es könnte einen besseren Weg geben, um dein _true_ Ziel zu erreichen. –

+0

Haben Sie andere Ideen für ein Plugin-System, wo ich in jeder Funktion eines Objekts "haken" kann. (Der Hooking-Teil funktioniert ganz gut, aber der Aufruf der Originalimplementierung ist der Schlüssel des Problems) – regexp

+0

Nachdem ich etwas Ähnliches gemacht habe, kann ich dir vielleicht etwas Zeit sparen: Du wirst schließlich eine Steinmauer treffen, wenn du zu variadischen Methoden kommst wie 'stringWithFormat:'). Wenn das ein Deal-Breaker für Sie ist, rate ich Ihnen, jetzt aufzugeben. – ipmcc

Antwort

4

Lass mich das in zwei Teile zerlegen, da ich einfach nicht die Verbindung zwischen deinen beiden Fragen bekommen kann.

I. Erstellen Sie eine neue Methode mit der „alten“ Implementierung und ein Schema zu Namen ändern, wie „___name“

, die ziemlich einfach ist, obwohl ich verstehe nicht, wie das in der Lage sein würde, Ihr Problem zu lösen . Sie können variadic Funktionsargumente immer noch nicht an eine solche Methode übergeben (und Sie haben Recht, das ist in C nicht möglich).

IMP swapImpForSelector(Class cls, SEL sel, IMP newImp) 
{ 
    Method m = class_getInstanceMethod(cls, sel); 
    IMP oldImp = method_setImplementation(m, newImp); 

    NSString *newSel = [NSString stringWithFormat:@"__prefixed_%@", NSStringFromSelector(sel)]; 
    const char *type = method_getTypeEncoding(m); 
    class_addMethod(cls, NSSelectorFromString(newSel), oldImp, type); 

    return oldImp; 
} 

II.Wenn Sie Varianzargumente zwischen Funktionen übergeben möchten, müssen Sie möglicherweise auf schweren Baugruppenhackage zurückgreifen. Glücklicherweise haben einige kluge Leute das schon für Sie getan.

Verwenden Sie entweder die NSInvocation class, oder wenn es nicht ausreicht, dann ist libffi noch niedriger.

+0

Oh, mit "class_addMethod" war einfach, danke dafür! Mein Plan erstellt tatsächlich eine NSInvocation dynamisch. Ich werde es versuchen. Danke! – regexp

+0

Das ist großartig! Vielen Dank! Funktioniert perfekt. Ich habe versucht, der ViewWillAppear einer statischen Bibliotheksklasse Funktionalität hinzuzufügen, auf die ich keinen Zugriff hatte. Dies ermöglichte mir, weiterhin ihren viewWillAppear zu feuern und anschließend weitere Funktionen hinzuzufügen. – Kevin

2

tun dies für beliebige Objekte wird sehr schwierig sein. Werfen Sie einen Blick auf AspectCocoa für etwas in dieser Richtung, aber Sie werden sehen, es funktioniert nicht so gut und wird nicht für den Einsatz in einer Produktionsumgebung empfohlen.

Aber für ein Plugin-System, dann würden Sie besser dran, nur so etwas wie eine PluggableObject Klasse definieren, die mit der Erweiterung im Auge ist. Vergessen Sie das Ausführen beliebiger Blöcke in der Mitte beliebiger Methoden - definieren Sie stattdessen bestimmte "Sockets", in die sich Dinge einfügen lassen, und eine Schnittstelle, der diese Funktionen folgen können, um die Funktionalität zu erhalten, die Sie unterstützen möchten. Es wird viel stabiler und einfacher sein, Dinge hinzuzufügen und zu reparieren.

+0

Das ist der Punkt, ich möchte die Erweiterung unter allen Umständen vermeiden. Zum Beispiel möchte ich einen UIVIewcontroller "pluginable" machen, so dass die Vererbung hier nicht passt. Vielleicht könnten "Kategorien" mir helfen? Aber ich habe nicht gefunden, wie man bestehende Methoden mit diesem Muster erweitert. Eine andere Idee ist die Verwendung von NSProxy, aber mit dieser Lösung verliere ich alle Compiler-Prüfungen ... – regexp

Verwandte Themen