2014-10-20 104 views
4

TestObj类是一个简单的类,它有一个方法doSomethingInBackground我发送它performSelectorInBackground使自己在后台线程中休眠5秒的方法。发送消息时会自动保留

@implementation TestObj 

- (void)dealloc 
{ 
    NSLog(@"%@, is main thread %u", NSStringFromSelector(_cmd), [NSThread isMainThread]) ; 
} 

- (void)doSomethingInBackground 
{ 
    [self performSelectorInBackground:@selector(backgroundWork) withObject:nil] ; 
} 

- (void)backgroundWork 
{ 
    sleep(5) ; 
} 

@end 

我alloc和初始化实例,并将其发送doSomethingInBackground消息,并指派nil它以尽快将其释放。

TestObj *obj = [[TestObj alloc] init] ; 
[obj doSomethingInBackground] ; 
obj = nil ; 

我发现dealloc将约5秒后obj = nil;运行,似乎是系统保留self时发送的方法[self performSelectorInBackground:@selector(backgroundWork) withObject:nil] ;backgroundWork返回后,该实例将被释放。

任何人都可以告诉我系统在这个背后做的工作。谢谢。

回答

2

-[NSObject performSelectorInBackground:withObject:]引擎盖下称-[NSThread initWithTarget:selector:object:]这确实保留了原有的接收器(如target参数这里传递)

NSThread文档:“这些对象的目标和论证的分离线程的执行过程中被保留,他们被释放时线程终于退出了。“

+0

如果我改变'[self performSelectorInBackground:@selector(backgroundWork)withObject:nil]''self performSelector:@selector(backgroundWork)withObject:nil afterDelay:5]'并且'backgroundWork'方法中什么也不做,结果是一样的。所以我认为这不是主要原因。 – KudoCC 2014-10-20 07:01:36

+1

@KudoCC' - [NSObject performSelector:withObject:afterDelay:]'直接保留接收器。在调用选择器之后,接收器通过'CFRunLoopTimerInvalidate'释放。所以我假设' - [NSObject performSelector:withObject:afterDelay:]'调度计时器来延迟方法调用。计时器保留目标。您可以通过禁用ARC,在'TestObj'中重载'-retain'和'-release'方法并在其中设置断点来轻松调查此问题。 (你可以运行这个https://gist.github.com/bartekchlebek/c36dba5d41bbe7b6fb5e) – 2014-10-20 07:13:16

+0

听起来很合理,我会试一试,谢谢。 – KudoCC 2014-10-20 07:21:59

2

从文档,

performSelectorInBackground:withObject:方法创建一个新的分离线程,并使用指定的方法为切入点新线程。例如,如果你有一些对象(由变量MyObj中表示)和对象有一个名为DoSomething的方法要在后台线程中运行,你可以使用下面的代码来做到这一点:

[myObj performSelectorInBackground:@selector(doSomething) withObject:nil]; 

调用此方法的效果与您将当前对象,选择器和参数对象作为参数调用NSThreaddetachNewThreadSelector:toTarget:withObject:方法相同。新线程立即使用默认配置生成并开始运行。

在detachNewThreadSelector:toTarget:withObject:文档,

aTarget和anArgument分离的线程的执行期间 保留的对象,然后被释放。一旦目标已经完成 执行aSelector方法,脱离的线程退出 (使用退出类方法)。

关于performSelector:AfterDelay:,

此方法设置了一个定时器来执行对 当前线程的运行循环的aSelector消息。定时器配置为以默认模式(NSDefaultRunLoopMode)运行于 。当定时器触发时,线程 尝试从运行循环中出列消息并执行 选择器。如果运行循环正在运行并且处于默认模式 ,则成功;否则,计时器等待直到运行循环是在默认模式

而且

计时器维护强引用此对象,直到它( 定时器)被无效。

如果您不希望您的OBJ得以保留,你可以使用弱引用的对象呢,

TestObj *obj = [[TestObj alloc] init] ; 
__weak typeof(obj) weakObj = obj; 
[weakObj doSomethingInBackground] ; 
obj = nil ; 
+0

你是不是指'[weakObj doSometingInBackground]'?目前,创建'weakObj'没有太大意义,因为它没有被使用。 – 2014-10-20 07:29:09

+0

哎呀,谢谢你指出。 – akshaynhegde 2014-10-20 07:29:59

+0

不客气! – 2014-10-20 07:31:05

1

在特殊情况下,像performSelector:已经在其他的答案被讨论,我认为这是有帮助的添加回答一般情况下:

会自行保留时发送消息给它

否。在手动保留计数和ARC self从不隐式保留。在执行方法期间,您必须注意消息的接收者未被释放。

虽然根据ARC self的类型很强,但实际上并未保留该对象。请参阅ARC documentation

+0

感谢一般情况下的答案和非常有用的文档。 – KudoCC 2014-10-20 08:00:31