2014-06-27 11 views
7

Ich weiß, es gibt unzählige Ressourcen auf Methode swizzling. Ist es jedoch möglich, eine Methode von einer privaten API zu swizzlen? Das Problem ist, dass es keine Header-Dateien gibt. Ich würde gerne eine Methode aus einer privaten Klasse in einem PrivateFramework swizzle wie (zufällige Beispiel) Message.framework MethodenMethode swizzling ein privates Framework iOS API

Dies ist für persönliche Tests, ich verstehe, dass es in Vergessenheit von Apple zurückgewiesen werden.

+0

Wenn Sie jemals versucht sind, etwas zu tun mit Private APIs, bei denen Sie das Gefühl haben, dass Sie Header-Dateien benötigen, können Sie sie einfach zurückentwickeln. Schauen Sie sich Tools wie 'class-dump' oder' class-dump-z' an. Viele Leute haben auch die Reverse-Engineering-Header online selbst gepostet. Fügen Sie diesen Header dann als Quelldatei in Ihr Projekt ein. Wenn Sie Probleme bekommen, wenn reverse-engineered Header nicht kompiliert werden, müssen Sie sie oft nur manuell überprüfen und "# imports", die Sie nicht benötigen, entfernen oder einfach die Header-Datei abschneiden, um nur die Funktion einzubeziehen, die Sie ' Interesse an. – Nate

+0

Das war, was ich anfangs tat und dachte, dass es einen einfacheren Weg geben muss. Jedenfalls bin ich dank Bryan (siehe unten) in der Lage, diese private API zu swizzlen. Das Problem ist, dass andere Frameworks nicht swizzled werden können. Wenn ich diese Methode direkt aus meinem Testcode herausrufe, bekomme ich die Swizzled-Funktion, aber wenn diese Methode in einem anderen Framework aufgerufen wird, ruft sie meine Swizzled-Funktion nicht auf. –

+0

Möglicherweise müssen Sie eine neue, weiterführende Frage stellen. Bitte sei auch spezifisch. Welche API möchten Sie swizzlen? Das ist nützlich, wenn Sie wissen, dass das Swizzling nicht funktioniert, wenn diese API innerhalb des Prozesses Ihrer App aufgerufen wird, aber direkt von einem Framework (nicht von Ihrem Code) aufgerufen wird? – Nate

Antwort

9

Sie können NSClassFromString verwenden, um Class abzurufen, und die Laufzeitbibliothek verwenden, um Methodenwechsel durchzuführen. Keine Header-Dateien erforderlich. Sie müssen nur den Klassennamen und die Methodensignatur kennen.

sel_getUid kann verwendet werden, wenn @selector(somePrivateMethod) Ihre Fehler zu somePrivateMethod geben ist nicht gültig Selektor (weil Header nicht verfügbar ist)

-Code genommen von my Xcode plugin

SEL sel = sel_getUid("codeDiagnosticsAtLocation:withCurrentFileContentDictionary:forIndex:"); 
Class IDEIndexClangQueryProviderClass = NSClassFromString(@"IDEIndexClangQueryProvider"); 

Method method = class_getInstanceMethod(IDEIndexClangQueryProviderClass, sel); 
IMP originalImp = method_getImplementation(method); 

IMP imp = imp_implementationWithBlock(^id(id me, id loc, id dict, IDEIndex *idx) { 
    id ret = ((id (*)(id,SEL,id,id,id))originalImp)(me, sel, loc, dict, idx); 

    // do work 

    return ret; 
}); 

method_setImplementation(method, imp); 
+0

Es kompiliert, aber nicht swizzle die Methode, die ich will ... –

+0

was ist passiert? Stellen Sie außerdem sicher, dass dieser Code ausgeführt wurde ... setzen Sie ihn in '+ [load]' –

+0

Der Code läuft definitiv.Um dies zu überprüfen, kann ich jedoch die private API aufrufen und sehen, ob sie die ursprüngliche Methode oder die swizzled-Methode aufruft. Ich habe das versucht und ich bekomme: "unerkannter Selektor an die Klasse geschickt", was bedeutet, dass meine private API überhaupt nicht angerufen wird. Ich habe die deklarierte Klasse zum Beispiel verwendet: [IDEIndexClangQueryProviderClass codeDiagnosticsAtLocation: 0 withCurrentFileContentDictionary: 0 forIndex: NULL] –

0

Erstellen Sie eine Kategorie für die Klasse, und fügen Sie die Deklaration für die Methode hinzu, die Sie aufrufen möchten. Dann können Sie eine Instanz der Klasse instanziieren und die Methode aufrufen.

Dies funktioniert auch für Unit-Test private Methoden in Ihrem Code.

+0

Ich versuche nicht, eine Methode hinzuzufügen, ich versuche, eine Methode zu ersetzen. Wollen Sie damit erklären, dass diese Methode die existierende Methode ersetzen würde? –

+0

Wenn Sie die Methode deklarieren, erhalten Sie Zugriff auf die Swizzle-Methode. In der Frage war die Beschwerde, dass es keine Header-Methode gab. Durch das Erstellen einer Kategorie können Sie effektiv eine Header-Methode für die Methode, die Sie swizzlen möchten, erstellen. – zaph

+0

Es gibt keinen Grund, hier Kategorien zu verwenden, und da dies nicht wirklich die Kategorien sind, ist es eine verwirrende Technik, die vermieden werden sollte. Wenn das Problem darin besteht, dass das Framework privat ist und Sie keine Header haben, erstellen Sie einfach den Header (oder die einzelnen Methoden, an denen Sie interessiert sind) zurück und fügen Sie diesen Header in Ihr Projekt ein. Es gibt keinen Grund dafür, dass es eine * Kategorie * ist. Der OP fehlt eine grundlegende Technik in der privaten API-Entwicklung, die darin besteht, Header umzukehren, die Sie benötigen. Das ist das Standardmuster für diese Klasse von Problemen. – Nate

Verwandte Themen