WebView通过WebEditingDelegate支持一种机制,该机制可使委托(delegate)人为WebView(或私有(private)WebHTMLView)接收的各种操作实现自定义行为。当一个 Action 如:

-(void)changeAttributes:(id)sender

WebHTMLView接收,并将其传递给委托(delegate)方法:
-(BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command

不幸的是,该机制没有提供原始 Action 方法中“sender”的传送。

对于绝大多数 Action 而言,发送者并不重要,但是对于changeAttributes和changeFont,例如,契约(Contract)要求接收者调用“sender”,例如convertAttributes:convertFont:

对于changeFont情况,事实证明,调用[[NSFontManager sharedFontManager] convertFont:]就足够了,因为恰巧这就是发送方。

changeAttributes的情况下,尤其是当删除线被更改时,发送方可能是私有(private)类“NSFontEffectsBox”,该类可能对应于负责更改删除线/etc设置的字体面板的小节。

不幸的是,调用[[NSFontManager sharedFontManager] convertAttributes:]无法获得预期的属性更改。这使一个有兴趣在有意义的地方实现此方法的代表感到困惑:
  • WebKit不会传达发送方,因此委托(delegate)无法进行契约(Contract)规定的[sender convertAttributes:]调用。
  • changeAttributes:调用被发送到私有(private)WebKit类WebHTMLView,该类不能被子类化,例如,自定义changeAttributes:的行为。
  • changeAttributes:调用的发送方NSFontEffectsBox是私有(private)类,无法访问,例如作为[NSFontEffectsBox sharedFontEffectsBox]

  • 简而言之:开发人员似乎没有办法有意义地覆盖changeAttributes:WebView行为。

    有任何想法吗?

    最佳答案

    这是邪恶的。一对适当的邪恶 Action (它们都不是特别干净或理想的):

  • 执行一些内联​​汇编程序以查看堆栈,以从调用方的堆栈(或应视情况而定的调用方)中读取sender参数。当然,这是假定在对%eax进行调用时,发件人位于堆栈中,而不是放在WebHTMLView中。但是,这将始终适用于PowerPC代码,因此可能在那里没有启动器。
  • 使用名为WebHTMLView之类的方法在__my_evil_hacky_nasty_ugly_changeAttributes_thing:上放置一个类别,并在运行时使用ObjC运行时中的method_exchangeImplementations()将您类别的实现与它们的实现交换。您的方法将变成changeAttributes:,而他们的方法将变成__my_evil_hacky_nasty_ugly_changeAttributes_thing:,然后可以调用该方法以传递原始调用。

  • 就像我说的那样,两者都不是特别理想,但是第二个优点是具有完整的运行时支持(即,运行时经过明确设计可以让您做到这一点),并且由于您在运行时查找类和方法,因此失败了-宽容。在这种情况下,失败会使您回到第一位。

    确实,它需要针对WebKit记录一个错误,以使它们传递给发送方,以使其完全有意义。您的重写版本可能会寻找方法-(BOOL)webView:(WebView*)webView doCommandBySelector:(SEL)selector sender:(id)sender并在找到后调用该方法,否则只需调用原始方法即可。这是Apple的代码应做的事情,TBH。

    10-08 03:42