C&C++   发布时间:2022-04-03  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了objective-c – cancelPreviousPerformRequestsWithTarget之后的self deallocs大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
使用ARC和iOS 6.1,我在这里一个简单的类来演示我的问题:

#import <GHUnitIOS/GHUnit.h>

@interface MyClass : NSObject
@property BOOL cancel;
@property BOOL dead;
-(void)doSomething;
-(void)reset;
-(void)logMe;
@end

@implementation MyClass

-(id)init {
    self = [super init];
    if(self) {
        [[NsnotificationCenter defaultCenter] addObserver:self SELEctor:@SELEctor(reset) name:@"dude" object:nil];
        NSLog(@"I'm alive");
    }
    return self;
}

-(void)dealloc {
    _dead = YES;
    [[NsnotificationCenter defaultCenter] removeObserver:self];
    [MyClass cancelPrevIoUsPerformrequestsWithTarget:self];
    NSLog(@"I'm dead");
}

-(void)doSomething {
    NSLog(@"dude:%d",_dead);
    if(!_cancel) {
        [self performSELEctor:@SELEctor(doSomething) withObject:nil afterDelay:0.2];
        NSLog(@"scheduled");
    }
    [self logMe];
}

-(void)reset {
    NSLog(@"reset");
    [MyClass cancelPrevIoUsPerformrequestsWithTarget:self];
    _cancel = YES;
    [self doSomething];
}

-(void)logMe {
    NSLog(@"logme");
}
@end

@interface ATest : GHTESTCase
@end

@implementation ATest

-(BOOL)shouldRunOnMainThread {return YES;}
-(void)setUpClass {}
-(void)tearDownClass {}
-(void)setUp {}
-(void)tearDown {}

-(void)TESTBlah {
    MyClass* blah = [[MyClass alloc] init];
    [blah doSomething];
    dispatch_after(dispatch_time(DISPATCH_TIME_Now,(int64_t)(1.0 * NSEC_PER_SEC)),dispatch_get_main_queue(),^(void){
        [[NsnotificationCenter defaultCenter] postNotificationName:@"dude" object:nil];
    });
    blah = nil;
}

@end

在测试中,MyClass被实例化,我启动了doSomething,它执行一些工作(即记录),然后在0.25s后调用自身,如果_cancel为false.同时,我在1.0s之后安排了一个通知(最终将_cancel设置为truE).然后我就出来了.

所以我的期望是performSELEctor创建的计时器:withObject:withDelay拥有对MyClass的引用.

但是,当我在启用僵尸的情况下运行此测试时,我得到了这个输出

调用cancelPrevIoUsPerformrequestsWithTarget之后,为什么self被释放:在reset方法中?

这个问题是ARC问题还是编码错误

解决方法

整洁的问题.我会称之为NsnotificationCenter中的一个错误.这是具有相同行为的代码的简化版本.我们所做的就是让自己听取通知,并通过一个强大的(静态)参来保持自己的生命.通知结束后,我们清除该引用. (在您的情况下,对象的最后一个强引用是在performSELEctor:machinery中; performSELEctor的目标:保留,当您取消它时,它会释放它对您的引用.)

@interface MyClass : NSObject
@end

static MyClass *instance;

@implementation MyClass

-(id)init {
    self = [super init];
    if(self) {
        [[NsnotificationCenter defaultCenter] addObserver:self SELEctor:@SELEctor(clearReferencE) name:@"dude" object:nil];
        NSLog(@"I'm alive");
        instance = self;
    }
    return self;
}

- (void)clearReference {
    instance = nil;
    [self logMe];
}

-(void)dealloc {
    [[NsnotificationCenter defaultCenter] removeObserver:self];
    NSLog(@"I'm dead");
}

-(void)logMe {
    NSLog(@"logme");
}

@end

// Test case
[[MyClass alloc] init];
[[NsnotificationCenter defaultCenter] postNotificationName:@"dude" object:nil];

这会在[self logMe]中导致僵尸消息.原因是在clearReference中,当我们执行instance = nil时;这是对我们的最后一个强引用,所以在我们调用[self logMe]之前我们被释放了.但是,你可能会问,为什么ARC@L_696_23@我们呢?

好吧,ARC永远不会保留自我,因为假设方法调用者对self有强烈的引用通常是安全的,并且如果每个方法都必须保留/释放self,那么会增加很多开销. (对于在ARC下编译的代码,这个假设几乎总是如此,因为要在对象上调用方法,首先需要对它进行引用.)不幸的是,NsnotificationCenter在调用方法之前不会保留您的对象.我称之为一个错误:在非ARC代码中,通常礼貌地确保在调用对象之前至少有一个临时强引用对象:

id objectToCall = ...;
[objectToCall retain];
[objectToCall performSELEctor:...]; // the actual callBACk
[objectToCall release];

这样的代码可以确保您看到的崩溃不会发生.显然,NsnotificationCenter没有这样做.您可以通过在Zombies工具中查看对象的保留历史来验证这一点.

既然你无法改变NsnotificationCenter,那么我之前使用过的一个难以理解的解决方法就是你可以解除分配并且你的调用者可能没有对你强有力的引用是这样的

- (void)clearReference {
    CFRetain((__bridge CFTypeRef)(self));
    instance = nil;
    [self logMe];
    CFRelease((__bridge CFTypeRef)(self));
}

这样,至少,您确信在方法结束之前不会取消分配.

大佬总结

以上是大佬教程为你收集整理的objective-c – cancelPreviousPerformRequestsWithTarget之后的self deallocs全部内容,希望文章能够帮你解决objective-c – cancelPreviousPerformRequestsWithTarget之后的self deallocs所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。