2013-01-02 53 views
5

從我這裏的問題:Without subclassing a UIView or UIViewController: possible to catch if a subview was added?如何將「method_getImplementation」和「method_setImplementation」移植到MonoTouch?

我想知道如何將代碼從答案移植到MonoTouch。它基本上用一個新方法替換一個方法,然後調用沒有繼承的舊方法。是否有可能讓這個指針在MonoTouch中工作?

//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

我也很好奇它是如何做到的。現在我只是連接一個ObjC庫來完成它。 –

回答

6

這看起來很適合我們提供通用機制來劫持方法。這裏是純C#代碼的實現,你可以在此期間使用:

[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保存嗎? – Krumelur

+0

如何重置劫持方法並使其再次使用原始方法? – Krumelur

+0

@Krumelur:只需調用'method_setImplementation(method,original_impl);'返回原來的。 –

0

我不得不作出以下修改米格爾的例子得到它的工作的設備上。

 [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"); 
     }