我有以下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
也就是说,没有发生混乱。为什么不呢?
答案 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()