2013-06-14 9 views
6

Chcę zbudować mały system wtyczek dla obiektów w obiekty-c.Objective-C dodaje funkcjonalność do każdej metody obiektu

Teraz utknąłem w punkcie, w którym chcę dynamicznie (w czasie wykonywania) dodać linię kodu do każdej funkcji dostępnej w obiekcie.

Grałem z biblioteką uruchomieniową, ale nie znalazłem rozwiązania.

Co próbowałem tak daleko jest taka:

 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); 

Mój problem jest, gdy istnieje więcej niż jeden argument nie mam szansy przekazać je do „func()”. AFAIK nie jest to możliwe w C.

Innym rozwiązaniem, o którym myślałem jest robienie magii za pomocą metody swizzling.

W krokach:

  1. Tworzenie „Method swizzle”, który po prostu wywołuje mój kod niestandardowy i wywołuje oryginalną metodę potem (za pomocą schematu nazewnictwa)
  2. zmienić IMP każdej funkcji z jednego w „Metoda swizzle”
  3. Utwórz nową metodę ze „starej” wdrożenie i zmiany nazwy na schemacie jak „___name”

w tym roztworze siedzę w punkcie 3. nie mam mana ged dynamicznie stworzyć kompletną nową metodę.

Czy ktoś może mi pomóc z moimi problemami powyżej lub ma inne rozwiązanie dla funkcji "catch all method".

Najlepiej byłoby coś takiego jak Invocation, która również przechwytuje już zdefiniowane funkcje.

Dzięki za pomoc!

+2

To dość intensywna czarna magia. Czy na pewno musisz to zrobić? Uwielbiam błąkać się z takimi rzeczami samemu i wspierać je, ale może istnieć lepszy sposób na osiągnięcie swojego "pomyślnego celu". –

+0

Czy masz jakieś inne pomysły na system wtyczek, w których mogę "hakować" w każdej funkcji obiektu. (Część zahaczająca działa całkiem nieźle, ale kluczem do problemu jest wywołanie oryginalnej implementacji). – regexp

+0

Po wykonaniu czegoś podobnego, może uda mi się zaoszczędzić trochę czasu: W końcu trafisz na ceglaną ścianę, gdy dojdziesz do metod variadycznych (tj. Metod jak 'stringWithFormat:'). Jeśli to dla ciebie złamanie umowy, radzę się teraz poddać. – ipmcc

Odpowiedz

4

Lemme podziel to na dwie części, ponieważ nie mogę uzyskać połączenia między tymi dwoma pytaniami.

I. Utwórz nową metodę ze „starej” Wdrożenie i zmiany nazwy na schemacie jak „___name”

to dość łatwe, chociaż nie rozumiem, jak to będzie w stanie rozwiązać problemu . Nadal nie możesz przekazać argumentów funkcji variadic do takiej metody (i masz rację, czego nie można zrobić w C).

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.Jeśli chcesz przekazać argumenty variadic pomiędzy funkcjami, może zajść potrzeba powrotu do ciężkiego hackażu. Na szczęście niektórzy mądrzy ludzie już to zrobili dla ciebie.

Użyj albo NSInvocation class, albo jeśli to nie wystarcza, wtedy libffi jest jeszcze niższy poziom.

+0

Och, użycie "class_addMethod" było łatwe, dziękuję za to! Mój plan naprawdę dynamicznie buduje NSInvocation. Dam temu szansę. Dzięki! – regexp

+0

To jest niesamowite! Dzięki! Działa świetnie. Próbowałem dodać funkcjonalność viewWillAppear klasy biblioteki statycznej, do której nie miałem dostępu. To pozwoliło mi nadal uruchamiać viewWillAppear i dodawać później więcej funkcji. – Kevin

2

Wykonanie tego dla dowolnych obiektów będzie dość trudne. Spójrz na AspectCocoa, aby dowiedzieć się czegoś podobnego, ale zobaczysz, że nie działa tak świetnie i nie jest zalecany do użytku w środowisku produkcyjnym.

Ale w przypadku systemu wtyczek lepiej byłoby po prostu zdefiniować klasę PluggableObject, która została zaprojektowana z myślą o rozszerzeniu. Zapomnij o uruchamianiu dowolnych bloków w środku arbitralnych metod - zamiast tego zdefiniuj określone "gniazda", w których rzeczy mogą się podłączyć, i interfejs, który te rzeczy mogą wykonać, aby uzyskać funkcjonalność, którą chcesz obsłużyć. Będzie to o wiele bardziej stabilne i łatwiejsze dodawanie i poprawianie.

+0

Właśnie o to chodzi, chcę uniknąć rozszerzenia w każdych okolicznościach. Na przykład chcę również, aby kontroler UIVIewcontroller był "wtyczalny", więc dziedziczenie się tutaj nie mieści. Może "Kategorie" mogłyby mi pomóc? Ale nie znalazłem, jak rozszerzyć istniejące metody z tym wzorem. Innym pomysłem jest używanie NSProxy, ale dzięki temu rozwiązaniu tracę wszystkie kontrole kompilatora ... – regexp

Powiązane problemy