我需要在类别的dealloc方法中执行操作。我尝试过调情,但这不起作用(也不是一个好主意)。
如果有人问,答案是否定的,我不能使用子类,这是专门针对某个类别的。
我想使用[NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:]
或[self performSelector:withObject:afterDelay:]
对延迟执行操作,并在dealloc上取消。
第一个问题是NSTimer
保留了我不想要的目标。 [self performSelector:withObject:afterDelay:]
未保留,但我需要能够使用[NSObject cancelPreviousPerformRequestsWithTarget:selector:object:]
方法调用dealloc
,否则我们会崩溃。
有关如何在类别上执行此操作的任何建议吗?
答案 0 :(得分:8)
我仍然认为将类子类化为更好并且不会弄乱运行时,但如果您确定需要在类别中进行操作,我会考虑您的选择。它仍然与运行时混淆,但比我想的更加安全。
考虑编写一个帮助类,比如调用它DeallocHook
,它可以附加到任何NSObject
,并在释放此NSObject
时执行操作。然后你可以做这样的事情:
// Instead of directly messing with your class -dealloc method, attach
// the hook to your instance and do the cleanup in the callback
[DeallocHook attachTo: yourObject
callback: ^{ [NSObject cancelPrevious... /* your code here */ ]; }];
您可以使用DeallocHook
:
objc_setAssociatedObject
@interface DeallocHook : NSObject
@property (copy, nonatomic) dispatch_block_t callback;
+ (id) attachTo: (id) target callback: (dispatch_block_t) block;
@end
实施将是这样的:
#import "DeallocHook.h"
#import <objc/runtime.h>
// Address of a static global var can be used as a key
static void *kDeallocHookAssociation = &kDeallocHookAssociation;
@implementation DeallocHook
+ (id) attachTo: (id) target callback: (dispatch_block_t) block
{
DeallocHook *hook = [[DeallocHook alloc] initWithCallback: block];
// The trick is that associations are released when your target
// object gets deallocated, so our DeallocHook object will get
// deallocated right after your object
objc_setAssociatedObject(target, kDeallocHookAssociation, hook, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return hook;
}
- (id) initWithCallback: (dispatch_block_t) block
{
self = [super init];
if (self != nil)
{
// Here we just copy the callback for later
self.callback = block;
}
return self;
}
- (void) dealloc
{
// And we place our callback within the -dealloc method
// of your helper class.
if (self.callback != nil)
dispatch_async(dispatch_get_main_queue(), self.callback);
}
@end
有关关联参考的详细信息,请参阅有关Objective-C runtime的Apple文档(尽管我说文档在此主题方面并不十分详细)。
我没有彻底测试过,但它似乎有效。我以为我会给你另一个方向来研究。
答案 1 :(得分:4)
我偶然发现了一个我以前从未见过的解决方案,似乎有效......
我有一个类别 - 正如人们经常做的那样 - 需要一些状态变量,因此我使用objc_setAssociatedObject
,如下所示:
Memento *m = [[[Memento alloc] init] autorelease];
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
而且,我需要知道我的类别扩展的实例何时被dealloc
编辑。在我的情况下,这是因为我在self
上设置观察者,并且必须在某个时刻删除那些观察者,否则我会收到NSKVODeallocateBreak
泄漏警告,这可能导致不好的事情。
突然间我突然意识到,因为我的关联对象被retain
编辑(因为使用了OBJC_ASSOCIATION_RETAIN_NONATOMIC
),所以它们也必须release
d,因此{{1}实际上我在我创建的用于存储状态值的简单存储类中实现了dealloc
方法。并且,我假设:我的关联对象必须在我的类别的实例之前被解除分配!因此,我可以让我的关联对象在他们意识到他们的时候通知他们的所有者{{ 1}}版!由于我已经有了保留的关联对象,我只需要添加一个dealloc
属性(不指定为dealloc
!),设置所有者,然后调用一些关联对象的owner
方法中所有者的方法。
这是我的类别的.m文件的修改部分,带有相关位:
retain
我在某处学习的模式(可能在S.O.上)使用工厂方法来获取或创建纪念品对象。现在它将所有者设置在纪念品上,而memento的dealloc
方法会回叫让所有者知道它正在#import <objc/runtime.h> // So we can use objc_setAssociatedObject, etc.
#import "TargetClass+Category.h"
@interface TargetClass_CategoryMemento : NSObject
{
GLfloat *_coef;
}
@property (nonatomic) GLfloat *coef;
@property (nonatomic, assign) id owner;
@end
@implementation TargetClass_CategoryMemento
-(id)init {
if (self=[super init]) {
_coef = (GLfloat *)malloc(sizeof(GLfloat) * 15);
}
return self;
};
-(void)dealloc {
free(_coef);
if (_owner != nil
&& [_owner respondsToSelector:@selector(associatedObjectReportsDealloc)]) {
[_owner associatedObjectReportsDealloc];
}
[super dealloc];
}
@end
@implementation TargetClass (Category)
static NSString *kMementoTagKey = @"TargetClass+Category_MementoTagKey";
-(TargetClass_CategoryMemento *)TargetClass_CategoryGetMemento
{
TargetClass_CategoryMemento *m = objc_getAssociatedObject(self, kMementoTagKey);
if (m) {
return m;
}
// else
m = [[[TargetClass_CategoryMemento alloc] init] autorelease];
m.owner = self; // so we can let the owner know when we dealloc!
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return m;
}
-(void) doStuff
{
CCSprite_BlurableMemento *m = [self CCSprite_BlurableGetMemento];
// do stuff you needed a category for, and store state values in m
}
-(void) associatedObjectReportsDealloc
{
NSLog(@"My associated object is being dealloced!");
// do stuff you need to do when your category instances are dealloced!
}
@end
编辑
注意事项:
dealloc
,否则它将不会自动保留和释放。dealloc
而不是所有者OBJC_ASSOCIATION_RETAIN_NONATOMIC
编辑,则会变得更加棘手......但您可以训练一个对象或另一个对象来忽略该事件dealloc
属性不能声明为dealloc
,否则您将真正创建一个强大的参考循环,这两个对象都不会被owner
编辑!retain
之前,dealloc
关联对象必须OBJC_ASSOCIATION_RETAIN_NONATOMIC
来记录,但它似乎是这样发生的,几乎必须是这种情况,至少是直观的。release
的成员对象,您将崩溃!我的猜测是它之后。这有点乱,因为你是双重链接你的对象,这要求你非常小心地保持这些引用。但是,它不涉及调动或对运行时的其他干扰 - 这只是依赖于运行时的某种行为。如果您已经有一个关联的对象,它似乎是一个方便的解决方案。在某些情况下,创建一个只是为了抓住你自己的dealloc
s!
答案 2 :(得分:2)
遗憾的是,您建议的解决方案无效:因为 NSTimer
保留其目标,目标将永远不会运行其dealloc
,直到计时器失效。目标的保留计数将始终悬停在1或更高,等待计时器释放它。你必须在 dealloc
之前到达计时器。 (在ARC之前,你可能覆盖retain
和release
并销毁计时器,尽管这确实不是一个好的解决方案。)
NSThread
also has this problem,解决方案很简单:一些重新设计将线程的控制器与“模型”分开。在这种情况下,创建和拥有线程或计时器的对象也不应该是计时器的目标。然后,代替你当前拥有的保留周期(计时器拥有拥有计时器的对象),你有一个很好的直线:控制器拥有拥有目标的计时器。外部对象只需要与控制器交互:当它被解除分配时,它可以关闭计时器,而不必使用覆盖dealloc
或其他内存管理方法来玩游戏。
这是处理此问题的最佳方法。如果由于某种原因你不能这样做 - 你说的是类别覆盖,所以显然你没有代表计时器目标的类的代码(但你仍然可以做一个控制器,即使在那种情况下) - 你可以使用弱引用。不幸的是,我不知道如何让NSTimer
对其目标进行弱引用,但GCD会通过dispatch_after()
给你一个合理的近似值。获取对目标的弱引用,并在您传递的块中专门使用它。 Block不会通过弱引用(NSTimer
的方式)保留对象,如果在Block运行之前对象已被释放,则弱引用当然是nil
,因此您可以安全地写下你喜欢的任何信息。