0

I have the following Xamarin.Mac code:

[Register("Swizzler")]
public class Swizzler : NSObject
{
    [DllImport("/usr/lib/libobjc.dylib")] public static extern IntPtr class_getInstanceMethod(IntPtr classHandle, IntPtr Selector);
    [DllImport("/usr/lib/libobjc.dylib")] public static extern bool method_exchangeImplementations(IntPtr m1, IntPtr m2);

    public void AttemptSwizzle()
    {
        var swizzledClassPtr = Class.GetHandle("Swizzled");
        var swizzlerClassPtr = Class.GetHandle("Swizzler");
        SwizzleInstanceMethod(swizzledClassPtr, new Selector("originalMethod"), swizzlerClassPtr, new Selector("newMethod"));

        var swizzled = new Swizzled();
        swizzled.PerformSelector(new Selector("originalMethod"));
    }

    internal void SwizzleInstanceMethod(IntPtr originalClassPtr, Selector originalSelector, IntPtr newClassPtr, Selector newSelector)
    {
        var originalMethod = class_getInstanceMethod(originalClassPtr, originalSelector.Handle);
        var swizzledMethod = class_getInstanceMethod(newClassPtr, newSelector.Handle);

        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

    [Export("newMethod")]
    public void NewMethod()
    {
        Console.WriteLine("New method called");
    }
}

[Register("Swizzled")]
internal class Swizzled : NSObject
{
    [Export("originalMethod")]
    public void OriginalMethod()
    {
        Console.WriteLine("Original method called");
    }
}

Code sample at https://github.com/alataffective/XamarinSwizzler.

When calling new Swizzler().AttemptSwizzle() I get the following output:

SomeMethod called

That is, the swizzling isn't happening. Why not?

fractor
  • 1,534
  • 2
  • 15
  • 30
  • Hi , could you share a sample link here ,this is a Xamarin.Mac app ? – Junior Jiang Dec 03 '19 at 09:10
  • Code sample at https://github.com/alataffective/XamarinSwizzler. Yes, Xamarin.Mac (original post updated). Thanks. – fractor Dec 03 '19 at 09:49
  • Thanks for sharing , I will check it . – Junior Jiang Dec 04 '19 at 03:21
  • Hi , after checking in Xamrin.Mac with multi-times , it seems swizzling method not wrok always . I think you can submit it in Github [here](https://github.com/xamarin/xamarin-macios) to follow it up , there will be more people to help find the solution . – Junior Jiang Mar 03 '20 at 01:21

1 Answers1

0

The problem is that you exchange implementations of exported C# methods. Exports share a single implementation, that performs it's own mapping from selectors to C# methods. Therefore, switching them has no effect. You can check it yourself when you call method_getImplementation() for both exported methods and compare returned values.

Just try swizzling original Objective-C methods of original Objective-C classes and you'll see that it works:

        public void AttemptSwizzle()
        {
            var swizzledClassPtr = Class.GetHandle("NSWindow");
            var swizzlerClassPtr = Class.GetHandle("NSWindow");
            SwizzleInstanceMethod(swizzledClassPtr, new Selector("title"), swizzlerClassPtr, new Selector("tabbingIdentifier"));

            var swizzled = new NSWindow();
            swizzled.TabbingIdentifier = "TabbingIdentifier";
            var result = swizzled.Title;
            Console.WriteLine($"title:{result}");
        }

If you want to swizzle original Objective-C method with a plain, not exported C# one, you still can, making use of the following methods:

Marshal.GetFunctionPointerForDelegate()
method_setImplementation()
method_getImplementation()
Jiri Volejnik
  • 1,034
  • 6
  • 9