ios Swizzing单个示例,而不是类

hjzp0vay  于 5个月前  发布在  iOS
关注(0)|答案(4)|浏览(45)

我在NSObject上有一个类别,它应该有一些东西。当我在一个对象上调用它时,我想覆盖它的dealloc方法来做一些清理。
我想用方法swizzling来实现它,但是不知道怎么做。我找到的唯一的例子是如何替换整个类的方法实现(在我的例子中,它会覆盖所有NSObjects的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

字符串

jhdbpxl9

jhdbpxl91#

你不能真正做到这一点,因为对象没有自己的方法表。只有类才有方法表,如果你改变这些方法表,它会影响该类的每个对象。不过有一个简单的方法:在运行时将对象的类更改为动态创建的子类。这种技术,也称为isa-swizzling,被Apple用来实现自动KVO。
这是一个强大的方法,它有它的用途。但对于您的情况,有一个更简单的方法使用关联对象。基本上,您使用objc_setAssociatedObject将另一个对象关联到您的第一个对象,该对象在其dealloc中进行清理。您可以在this blog post on Cocoa is my Girlfriend中找到更多细节。

6ovsh4lw

6ovsh4lw2#

方法选择是基于对象示例的 class 的,因此方法混合会影响同一个类的所有示例-正如您所发现的。
但是你可以改变一个示例的类,但是你必须小心!这里是大纲,假设你有一个类:

@instance MyPlainObject : NSObject

- (void) doSomething;

@end

字符串
现在,如果你想改变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);
}


现在,对于任何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];


(一些)警告:
如果子类重写或添加方法,并添加static(类)变量,您可以这样做。
你还需要一个子类来改变你想要改变的类的行为,你不能有一个单独的类来改变许多不同类的行为。

mcvgt66p

mcvgt66p3#

我做了一个swizzling API,它也具有特定于示例的swizzling功能。
它的工作原理是为您在运行时正在swizzling的特定示例创建一个动态子类。

vjrehmav

vjrehmav4#

我做了一个工具来做这件事。https://github.com/623637646/SwiftHook

try hookDeallocBefore(object: obj) {
  // obj will be released.
}

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

相关问题