2013-01-02 15 views
5

Sorumu burada: Without subclassing a UIView or UIViewController: possible to catch if a subview was added?MonoTouch'a "method_getImplementation" ve "method_setImplementation" nasıl bağlanır?

Yanıtın kodundan MonoTouch'a nasıl yönlendirileceğini merak ediyorum. Temel olarak yeni bir yöntemle bir yöntemi değiştirir ve alt sınıfı olmadan eski olanı çağırır. MonoTouch'da çalışmak için hokkabazlık eden bu işaretçiyi almak mümkün mü?

//Makes views announce their change of superviews 
Method method = class_getInstanceMethod([UIView class], @selector(willMoveToSuperview:)); 
IMP originalImp = method_getImplementation(method); 

void (^block)(id, UIView*) = ^(id _self, UIView* superview) { 
    [_self willChangeValueForKey:@"superview"]; 
    originalImp(_self, @selector(willMoveToSuperview:), superview); 
    [_self didChangeValueForKey:@"superview"]; 
}; 

IMP newImp = imp_implementationWithBlock((__bridge void*)block); 
method_setImplementation(method, newImp); 
+0

Ayrıca nasıl yapılabileceğini merak ediyorum. Şu anda sadece bunu yapan bir ObjC lib aleyhine bağ kuruyorum. –

cevap

6

Bu, yöntemleri kaçırmak için genel amaçlı bir mekanizma sağlamak için iyi bir aday gibi görünüyor.

[DllImport ("/usr/lib/libobjc.dylib")] 
    extern static IntPtr class_getInstanceMethod (IntPtr classHandle, IntPtr Selector); 
    [DllImport ("/usr/lib/libobjc.dylib")] 
    extern static Func<IntPtr,IntPtr,IntPtr> method_getImplementation (IntPtr method); 
    [DllImport ("/usr/lib/libobjc.dylib")] 
    extern static IntPtr imp_implementationWithBlock (ref BlockLiteral block); 
    [DllImport ("/usr/lib/libobjc.dylib")] 
    extern static void method_setImplementation (IntPtr method, IntPtr imp); 

    static Func<IntPtr,IntPtr,IntPtr> original_impl; 

    void HijackWillMoveToSuperView() 
    { 
     var method = class_getInstanceMethod (new UIView().ClassHandle, new Selector ("willMoveToSuperview:").Handle); 
     original_impl = method_getImplementation (method); 
     var block_value = new BlockLiteral(); 
     CaptureDelegate d = MyCapture; 
     block_value.SetupBlock (d, null); 
     var imp = imp_implementationWithBlock (ref block_value); 
     method_setImplementation (method, imp); 
    } 

    delegate void CaptureDelegate (IntPtr block, IntPtr self, IntPtr uiView); 

    [MonoPInvokeCallback (typeof (CaptureDelegate))] 
    static void MyCapture (IntPtr block, IntPtr self, IntPtr uiView) 
    { 
     Console.WriteLine ("Moving to: {0}", Runtime.GetNSObject (uiView)); 
     original_impl (self, uiView); 
     Console.WriteLine ("Added"); 
    } 
+0

App Store kaydetme olduğunu mu düşünüyorsunuz? – Krumelur

+0

Kaçırma yöntemini nasıl sıfırlarım ve orijinali tekrar kullanmamı nasıl sağlar? – Krumelur

+0

@Krumelur: Sadece orijinal olana dönmek için 'method_setImplementation (yöntem, original_impl); –

0

Ben bir cihazda çalışmasını sağlamak üzere Miguel'in Örneğin aşağıdaki değişiklikleri yapmak zorunda kaldı: Burada bu arada kullanabileceğiniz saf C# kodunda bir uygulamasıdır.

 [DllImport("/usr/lib/libobjc.dylib")] 
     extern static IntPtr class_getInstanceMethod(IntPtr classHandle, IntPtr Selector); 

     [DllImport("/usr/lib/libobjc.dylib")] 
     extern static IntPtr method_getImplementation(IntPtr method); 

     [DllImport("/usr/lib/libobjc.dylib")] 
     extern static IntPtr imp_implementationWithBlock(ref BlockLiteral block); 

     [DllImport("/usr/lib/libobjc.dylib")] 
     extern static void method_setImplementation(IntPtr method, IntPtr imp); 

     static IntPtr original_impl; 

     [MonoNativeFunctionWrapper] 
     public delegate void OriginalDelegate(IntPtr one,IntPtr two); 

     static void HijackWillMoveToSuperView() 
     { 
      var method = class_getInstanceMethod(new UIView().ClassHandle, new Selector("willMoveToSuperview:").Handle); 
      original_impl = method_getImplementation(method); 
      var block_value = new BlockLiteral(); 
      CaptureDelegate d = MyCapture; 
      block_value.SetupBlock(d, null); 
      var imp = imp_implementationWithBlock(ref block_value); 
      method_setImplementation(method, imp); 
     } 

     delegate void CaptureDelegate(IntPtr block,IntPtr self,IntPtr uiView); 

     [MonoPInvokeCallback(typeof(CaptureDelegate))] 
     static void MyCapture(IntPtr block, IntPtr self, IntPtr uiView) 
     { 
      Console.WriteLine("Moving to: {0}", Runtime.GetNSObject(uiView)); 

      var del = (OriginalDelegate) Marshal.GetDelegateForFunctionPointer (original_impl,typeof(OriginalDelegate)); 
      del (self, uiView); 

      Console.WriteLine("Added"); 
     }