- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
使用 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.25 秒后调用自身,如果 _cancel
为假。同时,我安排了一个通知在 1.0 秒后触发(最终将 _cancel
设置为 true)。然后我将 blah
归零。
所以我的期望是由 performSelector:withObject:withDelay
创建的计时器拥有对 MyClass
的引用。
但是,当我在启用僵尸的情况下运行此测试时,我得到以下输出:
2013-02-28 15:30:55.518 Tests[11946:c07] ATest/testBlah
2013-02-28 15:30:56.789 Tests[11946:c07] Re-running: ATest/testBlah
2013-02-28 15:30:56.790 Tests[11946:c07] I'm alive
2013-02-28 15:30:56.790 Tests[11946:c07] dude:0
2013-02-28 15:30:56.791 Tests[11946:c07] scheduled
2013-02-28 15:30:56.791 Tests[11946:c07] logme
2013-02-28 15:30:56.792 Tests[11946:c07] ATest/testBlah ✔ 0.00s
2013-02-28 15:30:56.991 Tests[11946:c07] dude:0
2013-02-28 15:30:56.992 Tests[11946:c07] scheduled
2013-02-28 15:30:56.992 Tests[11946:c07] logme
2013-02-28 15:30:57.193 Tests[11946:c07] dude:0
2013-02-28 15:30:57.194 Tests[11946:c07] scheduled
2013-02-28 15:30:57.194 Tests[11946:c07] logme
2013-02-28 15:30:57.395 Tests[11946:c07] dude:0
2013-02-28 15:30:57.395 Tests[11946:c07] scheduled
2013-02-28 15:30:57.396 Tests[11946:c07] logme
2013-02-28 15:30:57.596 Tests[11946:c07] dude:0
2013-02-28 15:30:57.597 Tests[11946:c07] scheduled
2013-02-28 15:30:57.597 Tests[11946:c07] logme
2013-02-28 15:30:57.792 Tests[11946:c07] reset
2013-02-28 15:30:57.793 Tests[11946:c07] I'm dead
2013-02-28 15:30:57.793 Tests[11946:c07] * -[MyClass doSomething]: message sent to deallocated instance 0xb584880
为什么在 reset
方法中调用 cancelPreviousPerformRequestsWithTarget:
后 self
被释放?
这个问题是 ARC 问题还是编码错误?
最佳答案
很好的问题。我将此称为 NSNotificationCenter 中的错误。这是具有相同行为的代码的简化版本。我们所做的就是让自己设置为收听通知,并通过一个强(静态)引用让自己保持活力。当通知关闭时,我们清除该引用。 (在你的情况下,对你的对象的最后一个强引用是在 performSelector:
机制中; 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 不支持我们?
好吧,ARC 从不保留 self,因为假设方法的调用者对 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 释放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15146235/
让我们写一个简单的类在我的脑海中解释: class SomeClass { var happyToUsed = 10 } 并创建一个对象 let someObject = SomeClass(
采用 self 的方法与采用 &self 甚至 &mut self 的方法有什么区别? 例如 impl SomeStruct { fn example1(self) { } fn ex
请观察以下代码(Win10上的python 3.6,PyCharm),函数thread0(self)作为线程成功启动,但是 thread1(self)似乎与thread0(self)不同已设置。 se
backbone.js 开始于: //Establish the root object, `window` (`self`) in the browser, or `global` on the s
做的事: self = self.init; return self; 在 Objective-C 中具有相同的效果: self.init() 快速? 例如,在这种情况下: else if([form
我查看了关于堆栈溢出的一些关于使用[weak self]和[unowned self]的问题的评论。我需要确保我理解正确。 我正在使用最新的 Xcode - Xcode 13.4,最新的 macOS
我面临在以下模型类代码中的 self.init 调用或分配给 self 之前使用 self 的错误tableview单元格项目,它发生在我尝试获取表格单元格项目的文档ID之后。 应该做什么?请推荐。
晚上好。 我对在 Swift 中转义(异步)闭包有疑问,我想知道哪种方法是解决它的最佳方法。 有一个示例函数。 func exampleFunction() { functionWithEsca
我需要在内心深处保持坚强的自我。 我知道声明[weak self]就够了外封闭仅一次。 但是guard let self = self else { return }呢? ,是否也足以为外部闭包声明一
代码 use std::{ fs::self, io::self, }; fn rmdir(path: impl AsRef) -> io::Result { fs::remo
我检查了共享相同主题的问题,但没有一个解决我遇到的这种奇怪行为: 说我有一个简单的老学校struct : struct Person { var name: String var age:
我应该解释为什么我的问题不是重复的:TypeError: can only concatenate list (not “str”) to list ...所以它不是重复的,因为该帖子处理代码中出现的
我有一个 trait,它接受一个类型参数,我想说实现这个 trait 的对象也会符合这个类型参数(使用泛型,为了 Java 的兼容性) 以下代码: trait HandleOwner[SELF
这个问题在这里已经有了答案: Why would a JavaScript variable start with a dollar sign? [duplicate] (16 个答案) 关闭 8
我总是找到一些类似的代码newPromise.promiseDispatch.apply(newPromise, message),我不明白为什么不使用newPromise.promiseDispat
我看到类似的模式 def __init__(self, x, y, z): ... self.x = x self.y = y self.z = z ... 非
mysql查询示例: SELECT a1.* FROM agreement a1 LEFT JOIN agreement a2 on a1.agreementType = a2.agreementTy
self.delegate = self; 这样做有什么问题吗?正确的做法是什么? 谢谢,尼尔。 代码: (UITextField*)initWith:(id)sender:(float)X:(flo
为什么要声明self在类中需要的结构中不需要?我不知道是否还有其他例子说明了这种情况,但在转义闭包的情况下,确实如此。如果闭包是非可选的(因此是非转义的),则不需要声明 self在两者中的任何一个。
这个问题已经有答案了: What does the ampersand (&) before `self` mean in Rust? (1 个回答) 已关闭去年。 我不清楚 self 之间有什么区别
我是一名优秀的程序员,十分优秀!