调整单个实例,而不是类

问题描述 投票:0回答:4

我在 NSObject 上有一个类别,它应该包含一些东西。当我在一个对象上调用它时,我想重写它的 dealloc 方法来进行一些清理。

我想使用方法调配来做到这一点,但不知道如何做。我发现的唯一示例是如何替换整个类的方法实现(在我的例子中,它将覆盖所有 NSObject 的 dealloc - 我不想这样做)。

我想重写 NSObject 特定实例的 dealloc 方法。

@interface NSObject(MyCategory)
-(void)test;
@end

@implementation NSObject(MyCategory)
-(void)newDealloc
{
  // do some cleanup here
  [self dealloc]; // call actual dealloc method
}
-(void)test
{
  IMP orig=[self methodForSelector:@selector(dealloc)];
  IMP repl=[self methodForSelector:@selector(newDealloc)];
  if (...)   // 'test' might be called several times, this replacement should happen only on the first call
  {
     method_exchangeImplementations(..., ...);
  }
}
@end
objective-c ios methods categories swizzling
4个回答
20
投票

你不能真正做到这一点,因为对象没有自己的方法表。只有类才有方法表,如果更改这些方法表,它将影响该类的每个对象。不过,有一种简单的方法可以解决这个问题:在运行时将对象的类更改为动态创建的子类。这种技术也称为 isa-swizzling,Apple 使用它来实现自动 KVO。

这是一个强大的方法,它有它的用途。但对于您的情况,有一种使用关联对象的更简单的方法。基本上,您使用

objc_setAssociatedObject
将另一个对象关联到第一个对象,该对象在其
dealloc
中进行清理。您可以在这篇关于 Cocoa 是我女朋友的博客文章中找到更多详细信息。


12
投票

方法选择基于对象实例的class,因此方法调配会影响同一类的所有实例 - 正如您所发现的。

但是你可以改变实例的类,但是你必须小心!这是大纲,假设你有一个课程:

@instance MyPlainObject : NSObject

- (void) doSomething;

@end

现在,如果您只想更改

MyPlainObject
的某些实例,则首先定义一个子类:

doSomething

现在您可以清楚地创建 
@instance MyFancyObject: MyPlainObject - (void) doSomething; @end

的实例,但我们需要做的是获取

MyFancyObject
预先存在的 实例并将其放入
MyPlainObject
中,以便我们获得新的行为。为此,我们可以混合类,将以下内容添加到
MyFancyObject

MyFancyObject

现在,对于 
static Class myPlainObjectClass; static Class myFancyObjectClass; + (void)initialize { myPlainObjectClass = objc_getClass("MyPlainObject"); myFancyObjectClass = objc_getClass("MyFancyObject"); } + (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy { object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass); }

的任何 original 实例,您可以切换为

MyPlainClass
,反之亦然:

MyFancyClass

(一些)注意事项:

如果子类重写或添加方法,并添加

MyPlainClass *mpc = [MyPlainClass new]; ... // masquerade as MyFancyClass [MyFancyClass changeKind:mpc fancy:YES] ... // mpc behaves as a MyFancyClass // revert to true nature [MyFancyClass changeKind:mpc: fancy:NO];

(类)变量,您可以执行此操作。


您还需要为您希望更改其行为的每个类创建一个子类,您不能拥有一个可以更改许多不同类的行为的类。


1
投票
https://github.com/JonasGessner/JGMethodSwizzler

它的工作原理是为运行时正在调配的特定实例创建动态子类。


0
投票
https://github.com/623637646/SwiftHook

static

参考这个单元测试。
https://github.com/623637646/SwiftHook/blob/9d2d85a64059617f8272d01ff59c10ede7305a48/SwiftHookTests/SwiftAPITests/HookSpecificInstanceTests.swift#L254

© www.soinside.com 2019 - 2024. All rights reserved.