捕获 Objective C 类

发布于 2024-11-07 15:54:43 字数 437 浏览 2 评论 0原文

我试图捕获/覆盖在我无法访问的对象中调用的某些方法。我希望能够对系统对象中实例化的对象进行子类化。

例如,当我实例化 UIWebView 时,它在内部实例化 UIScrollView。我希望能够子类化 UIScrollView,这样我就可以通过子类化它来修改其行为的某些方面。这可能吗?

我一直在尝试通过使用类别@implementation UIScrollView (MyScrollView),然后覆盖+分配,并返回我自己的对象(UIScrollView 的子类)来做到这一点。但是,我遇到了一些问题。基本上,我似乎无法达到原始的 [UIScrollView alloc] 方法。也许我可以实现自己的 + alloc 方法来复制 [NSObject alloc] 行为?

有没有更简单的方法来做到这一点?我目前的方法有什么问题?

最后,这样做能通过苹果的审核吗?我不会调用任何未记录的内容。

I'm trying to catch/override certain methods that get called within objects I have no access to. I would like to be able to subclass objects that are instantiated within system objects.

For example, when I instantiate a UIWebView, it internally instantiates a UIScrollView. I'd like to be able to subclass that UIScrollView so I can modify certain aspects of its behavior by subclassing it. Is that possible?

I have been trying to do this by using a category, @implementation UIScrollView (MyScrollView), then overriding + alloc, and returning my own object which is a subclass of UIScrollView. However, I'm running into some problems. Basically, I can't seem to reach the original [UIScrollView alloc] method. Perhaps I could implement my own + alloc method that would replicate the [NSObject alloc] behavior?

Is there an easier way to do this? What is wrong with my current approach?

Finally, would doing this pass Apple's review process? I am not calling anything that is undocumented.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

相守太难 2024-11-14 15:54:43

类别的问题是,它们不允许适当的覆盖,因为您将无法再访问原始方法。

要实现这样的(原谅我的法语)肮脏技巧,你可以尝试 MethodSwizzling< /a>.作为最后的手段,这有时非常有价值。我过去主要使用它进行调试,也用于逆向工程。

对于您关于 Apple 审批流程的问题,我猜想,当以如此激进的方式更改 UIWebView 的默认实现时,您遇到麻烦的可能性相当高。

The problem of categories is, they are not allowing proper overriding as you will not be able to reach the original methods anymore.

To achieve such (forgive my french) dirty tricks, you could try MethodSwizzling. As a last resort, this sometimes is of great value. I have used it in the past for debugging mainly and also for reverse engineering.

For your question on the Apple approval process, I would guess that your chances of getting into trouble are pretty high when changing the default implementation of a UIWebView in such radical way.

时光暖心i 2024-11-14 15:54:43

不要使用类别来破坏现有方法。那就是疯狂。

在您的情况下,我要做的就是通过操作视图层次结构而不是分配机制,将滚动视图替换为自定义滚动视图子类的实例。

Don't use a category to clobber existing methods. That way lies madness.

What I'd do in your situation is replace the scrollview with an instance of your custom scrollview subclass by manipulating the view hierarchy, not the allocation mechanism.

惟欲睡 2024-11-14 15:54:43

@Till 的 Method Swizzling 方法就是实现这一目标的方法。迈克·阿什 (Mike Ash) 拥有 关于如何做到这一点的更好的文章。我目前赞成他的直接覆盖方法,尽管我对其进行了一些修改,如下所示:

#import <objc/runtime.h>

@interface NSObject (RNSwizzle)
+ (IMP)swizzleSelector:(SEL)origSelector withIMP:(IMP)newIMP;
@end

@implementation NSObject (RNSwizzle)

+ (IMP)swizzleSelector:(SEL)origSelector withIMP:(IMP)newIMP {
  Class class = [self class];
  Method origMethod = class_getInstanceMethod(class, origSelector);
  IMP origIMP = method_getImplementation(origMethod);

  if(!class_addMethod(self, origSelector, newIMP,
                      method_getTypeEncoding(origMethod)))
  {
    method_setImplementation(origMethod, newIMP);
  }

  return origIMP;
}

@end

根据他的示例,您可以这样使用我的方法:

gOrigDrawRect = [UIView swizzleSelector:@selector(drawRect:)
                                withIMP:OverrideDrawRect];

这些都是记录在案的调用,并且不使用 Apple 私有 API。苹果有时确实会拒绝那些对预期 UI 行为做出太大改变的应用程序(无论它们是如何做到的),但我已经毫无问题地交付了经过相当戏剧性的幕后 UI 修改的应用程序,并且这不依赖于私有蜜蜂。

这并不意味着 Method Swizzling 是稳定的。这是一种脆弱的技术,确实会让您更加依赖未记录的内部结构。如果苹果做出改变,这可能意味着你必须更加努力。

(深呼吸)

这里您可以使用另一种方法,只要您不需要 ivar 存储即可。您可以抓取滚动视图并将其类混合到您自己的子类中。我有没有提到过你必须没有自己的 ivar 存储空间?如果您在此分配一个 ivar,将会产生一些令人费解的错误。 Deusty 在他的博客中对此进行了很好的阐述。 (我在他的代码中添加了 ivar,并遇到了上面提到的令人难以置信的令人费解的错误。)

同样,这是一种脆弱、疯狂、危险的技术。有时它也可能是满足您需要的最优雅且可维护的方式。

@Till's approach of Method Swizzling is how you achieve this. Mike Ash has one of the better write-ups of how to do it. I currently favor his Direct Override approach, though I've reworked it a little as follows:

#import <objc/runtime.h>

@interface NSObject (RNSwizzle)
+ (IMP)swizzleSelector:(SEL)origSelector withIMP:(IMP)newIMP;
@end

@implementation NSObject (RNSwizzle)

+ (IMP)swizzleSelector:(SEL)origSelector withIMP:(IMP)newIMP {
  Class class = [self class];
  Method origMethod = class_getInstanceMethod(class, origSelector);
  IMP origIMP = method_getImplementation(origMethod);

  if(!class_addMethod(self, origSelector, newIMP,
                      method_getTypeEncoding(origMethod)))
  {
    method_setImplementation(origMethod, newIMP);
  }

  return origIMP;
}

@end

Given his example, you would use my method this way:

gOrigDrawRect = [UIView swizzleSelector:@selector(drawRect:)
                                withIMP:OverrideDrawRect];

These are all documented calls and do not make use of Apple private APIs. Apple does sometimes reject apps that make too dramatic a change to expected UI behaviors (regardless of how they do it), but I've shipped stuff with pretty dramatic under-the-covers UI modifications without trouble, and this does not rely on private APIs.

That doesn't mean that Method Swizzling is stable. It's a fragile technique and does make you more reliant on undocumented internals. It may mean you have to scramble more if Apple changes something.

(Deep breath)

There's another approach you can use here, as long as you require no ivar storage. You can grab the scroll view and class swizzle it to your own subclass. Did I mention you must have no ivar storage of your own? The bugs you'll create if you allocate an ivar in this and mind-bending. Deusty writes this up well in his blog. (It was his code that I added an ivar to and encountered the incredibly mind-bending bugs mentioned above.)

Again, this is a fragile, crazy, dangerous technique. It also can sometimes be the most elegant and maintainable way to do what you need.

泅人 2024-11-14 15:54:43

我实际上还没有做到这一点,所以我不能 100% 确定,但我相信你可以做你想做的事情,但我不认为你可以用你目前的方法做到这一点。我相信你必须使用 Objective-C 运行时来拦截方法。如果您还没有阅读过目标-C 运行时参考

好的,这就是可以,那么你应该你能逃脱惩罚吗?

通过这样做你完成的任何事情都可能会非常好。脆弱的。 Apple 对框架所做的任何未来更改都可能会破坏您的拦截代码。因此,就您个人而言,我会将其归档为“非常可疑”,以确定您是否应该考虑发布依赖于此的产品。

至于你能逃脱惩罚吗?显然,我不能代表苹果公司说话,但我打赌不会。您是对的,严格来说您没有调用未记录的代码,但您正在干扰私有方法的功能,因此您肯定违背了 App Store 条款的精神

当然,这一切只是我个人的看法,大家可以借鉴。

I have not actually done it, so I am not 100% certain, but I believe you can do what your are trying to do, but I do not think you can do so with your current approach. I believe you will have to use the Objective-C runtime to intercept methods. If you haven't already, read The Objective-C Runtime Reference.

OK, that's the can now what about the should you and will you get away with it?

Anything you accomplish by doing this will probably be very fragile. Any future changes Apple make to the frameworks may break your intercept code. So personally, I would file this under "very dubious" as to whether you should consider releasing a product that relies on this.

As for will you get away with it? Obviously, I can't speak for Apple, but my bet is no. You are correct that strictly speaking your are not calling undocumented code, but you are interfering with the functioning of private methods, so you are certainly flying in the face of the spirit of the App Store terms.

Of course, all this is merely my opinion, take from it what you will.

洋洋洒洒 2024-11-14 15:54:43

我找到了一个相对简单、相当安全的解决方案。它并不完美,并非在所有情况下都有效,但在某些情况下可能有用。

基本上,我正在做的是使用一个类别来捕获我想要的类的+分配调用。然后我实现自己的 + alloc 方法,该方法简单地分配并返回我的子类类型的对象。然后,我可以重写子类对象中的任何方法,并从 UIWebView 正确调用它们。这是一种自动子类化机制:目标类(在本例中为 UIScrollView)的每个实例都会自动子类化,即使其他人创建了它。

我的 + alloc 方法如下所示:

+ (id) alloc
{
    return class_createInstance([MyUIScrollView class], 0);
}

然后,我实现一个简单的 MyUIScrollView 类,该类继承自 UIScrollView 并允许我重写可能需要的任何方法,这使我可以更好地控制父 UIWebView 的行为。凉爽的!

一些问题:

  • 如果您的应用程序中有多个 UIScrollView,您需要小心,因为它们现在基本上都会变成 M​​yUIScrollView!解决方案是在 MyUIScrollView 中设置一个标志,以确保在实例不属于您的特定 UIWebView 的情况下它处于休眠状态(基本上只是将所有内容转发到超类)。这可以通过对 UIWebView 进行子类化并覆盖其视图层次结构方法(addSubview: 等)轻松完成,以便一旦 UIWebView 实例化并将其内部 UIScrollView 添加到视图层次结构中,您就可以在子类实例中设置一个标志告诉它做你想要做的任何事情。

  • 这仅适用于不在应用程序中任何位置对目标类(UIScrollView 等)进行子类化的应用程序!例如,我能够使用这种技术成功地将自己挤入 UIWebView 的 UIScrollView 中,并重写我想要在其中使用的任何方法,但是如果我的应用程序中的某些其他组件子类 UIScrollView (包括我自己的代码),我将遇到麻烦,因为我基本上禁用了这个类的所有子类化。应用程序中的所有 UIScrollView 都将是 MyUIScrollView 类型!

底线:这不是推荐的方法,但它看起来是解决此问题的最干净的方法。

I've found a relatively easy, fairly safe solution. It's not perfect and won't work in all cases, but might be useful in some cases.

Basically, what I'm doing is using a category to catch my desired class's + alloc calls. Then I implement my own + alloc method, which simply allocates and returns an object of my subclassed type. I can then override any method in my subclassed object and they get called correctly from UIWebView. This is an automated subclassing mechanism: Every instance of the target class (in this case, UIScrollView) gets automatically subclassed, even if someone else creates it.

Here's what my + alloc method looks like:

+ (id) alloc
{
    return class_createInstance([MyUIScrollView class], 0);
}

I then implement a simple MyUIScrollView class that inherits from UIScrollView and lets me override any methods I may need, which gives me much more control over the behavior of the the parent UIWebView. Cool!

A couple of catches:

  • If your application has more than one UIScrollViews in it, you need to be careful because every single one of them would now essentially become MyUIScrollView! The solution is to have a flag within MyUIScrollView that ensures that it's dormant (basically just forwards everything to the superclass) in cases where the instance is not owned by your specific UIWebView. This is easily done by subclassing the UIWebView, and overriding its view hierarchy methods (addSubview:, etc.) so that once UIWebView instantiates and adds its internal UIScrollView to the view hierarchy, you'll be able to set a flag in your subclassed instance to tell it to do whatever you're looking to do.

  • This only works for applications that do not subclass the target class (UIScrollView, etc.) anywhere in the application! For example, I am successfully able to squeeze myself into the UIWebView's UIScrollView using this technique and override whatever methods I'd like to within it, but if some other component in my application subclasses UIScrollView (including my own code), I will run into trouble because I am essentially disabling all subclassing for this class. All UIScrollViews in the application are going to be of type MyUIScrollView!

Bottom line: This is not a recommended approach, but it looks like the cleanest way to address this issue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文