Xamarin方法不起作用。为什么不?

时间:2019-12-02 15:25:23

标签: objective-c xamarin method-swizzling

我有以下Xamarin.Mac代码:

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

位于https://github.com/alataffective/XamarinSwizzler的代码示例。

调用new Swizzler().AttemptSwizzle()时,我得到以下输出:

SomeMethod called

也就是说,没有发生混乱。为什么不呢?

1 个答案:

答案 0 :(得分:0)

问题在于您交换了导出的 C# 方法的实现。导出共享一个实现,执行它自己的从选择器到 C# 方法的映射。因此,切换它们没有任何效果。您可以在为两个导出方法调用 method_getImplementation() 并比较返回值时自行检查。

只需尝试混合原始 Objective-C 类的原始 Objective-C 方法,您就会发现它有效:

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

如果你想将原始的 Objective-C 方法与一个普通的、不是导出的 C# 方法混合,你仍然可以使用以下方法:

Marshal.GetFunctionPointerForDelegate()
method_setImplementation()
method_getImplementation()