如何在类类别中访问dealloc方法?

Guy*_*gus 5 cocoa-touch memory-management objective-c nstimer ios

我需要在类别的dealloc方法中执行操作.我尝试过调配,但这不起作用(也不是一个好主意).

如果有人问,答案是否定的,我不能使用子类,这是专门针对一个类别的.

我想在延迟时使用[NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:]or 执行操作,[self performSelector:withObject:afterDelay:]并在dealloc上取消它.

第一个问题是NSTimer保留了我不想要的目标.[self performSelector:withObject:afterDelay:]不保留,但我需要能够调用[NSObject cancelPreviousPerformRequestsWithTarget:selector:object:]dealloc方法或我们得到一个崩溃.

有关如何在类别上执行此操作的任何建议吗?

Ego*_*sev 8

我仍然认为将类子类化为更好并且不会弄乱运行时,但如果您确定需要在类别中进行操作,我会为您考虑选项.它仍然与运行时混乱,但比我想的更加安全.

考虑编写一个帮助程序类,比如说它DeallocHook可以附加到任何一个帮助程序类,NSObjectNSObject在解除分配时执行一个操作.然后你可以做这样的事情:

// 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 */ ]; }];
Run Code Online (Sandbox Code Playgroud)

您可以实现DeallocHook使用objc_setAssociatedObject:

@interface DeallocHook : NSObject
@property (copy, nonatomic) dispatch_block_t callback;

+ (id) attachTo: (id) target callback: (dispatch_block_t) block;

@end
Run Code Online (Sandbox Code Playgroud)

实现将是这样的:

#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
Run Code Online (Sandbox Code Playgroud)

有关关联引用的更多信息,请参阅Apple关于Objective-C运行时的文档(尽管我说文档对于此主题并不是非常详细).

我没有对此进行过彻底的测试,但似乎有效.只是想我会给你另一个方向来研究.


S'p*_*'Kr 4

我只是偶然发现了一个我以前从未见过的解决方案,并且似乎有效......

我有一个类别(正如人们经常做的那样)需要一些状态变量,所以我使用objc_setAssociatedObject,如下所示:

Memento *m = [[[Memento alloc] init] autorelease];
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Run Code Online (Sandbox Code Playgroud)

而且,我需要知道我的类别扩展的实例何时被dealloc编辑。就我而言,这是因为我设置了观察者self,并且必须在某个时候删除这些观察者,否则我会收到NSKVODeallocateBreak泄漏警告,这可能会导致不好的结果。

突然我意识到,由于我的关联对象正在被retain编辑(因为使用OBJC_ASSOCIATION_RETAIN_NONATOMIC),它们也必须被release编辑,因此也被dealloc编辑......事实上,我已经dealloc在我创建的用于存储的简单存储类中实现了一个方法我的国家价值观。而且,我假设:我的关联对象必须我的类别实例之前被释放!因此,当我的关联对象意识到它们正在被编辑时,我可以让它们的所有者通知它们dealloc!由于我已经拥有保留的关联对象,因此我只需添加一个owner属性(未指定retain!),设置所有者,然后在关联对象的方法中调用所有者的某些方法dealloc

这是我的类别的 .m 文件的修改部分,其中包含相关位:

#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
Run Code Online (Sandbox Code Playgroud)

我在某处(可能是在 SO 上)学到的模式使用工厂方法来获取或创建纪念品对象。现在,它将所有者设置为备忘录,并且备忘录的dealloc方法会回调以让所有者知道它正在被dealloc编辑

注意事项:

  • 显然,您必须将关联对象设置为OBJC_ASSOCIATION_RETAIN_NONATOMIC,否则它不会自动为您保留和释放。
  • 如果您的备忘录/状态关联对象dealloc在其他情况下被编辑而不是所有者被dealloc编辑,那么这会变得更加棘手......但是您可以训练一个对象或另一个对象来忽略该事件。
  • owner属性不能声明为retain,否则您将真正创建一个强引用循环,并且两个对象都没有资格被dealloc编辑!
  • 我不知道是否有记录表明OBJC_ASSOCIATION_RETAIN_NONATOMIC关联对象必须release在所有者完全dealloc编辑之前进行编辑,但似乎是这样发生的,而且几乎一定是这样,至少从直觉上来说是这样。
  • 我不知道是否associatedObjectReportsDealloc会在 的 dealloc 方法之前之后调用- 这可能很重要!如果它之后运行,如果您尝试访问 的成员对象,您将会崩溃!我的猜测是之后。TargetClassTargetClass

这有点混乱,因为您正在双重链接对象,这需要您非常小心地保持这些引用的直线。但是,它不涉及混合或对运行时的其他干扰——这仅依赖于运行时的特定行为。如果您已经有关联的对象,这似乎是一个方便的解决方案。在某些情况下,可能值得创建一个来捕获您自己的dealloc