0

我有以下 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

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

4

1 回答 1

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

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

Marshal.GetFunctionPointerForDelegate()
method_setImplementation()
method_getImplementation()
于 2021-03-13T14:37:38.147 回答