我有一个方法可以为串行NSOperationQueue添加操作。由于我想定期调用该方法,我使用调度源定时器。
但是,也可以响应用户操作调用此方法。当发生这种情况时(例如,由于计时器而调用方法之前的一刻),我会延长计时器的开火日期。
问题是我写的代码有一个保留周期,我不明白在哪里。
以下是演示此问题的简化示例(不要忘记将部署SDK设置为10.7):
#import <Foundation/Foundation.h>
@interface MyObject : NSObject
@end
@implementation MyObject
{
NSOperationQueue *_queue;
dispatch_source_t _timer;
}
- (id)init
{
self = [super init];
if (self)
{
_queue = [[NSOperationQueue alloc] init];
[_queue setMaxConcurrentOperationCount:1];
}
return self;
}
- (void)dealloc
{
NSLog(@"dealloc");
[_queue cancelAllOperations];
dispatch_source_cancel(_timer);
dispatch_release(_timer);
}
- (void)scheduleTimer
{
if (_timer)
{
dispatch_source_cancel(_timer);
dispatch_release(_timer);
}
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0,
0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
if (_timer)
{
__weak MyObject *selfBlock = self;
dispatch_source_set_event_handler(_timer, ^{
dispatch_source_cancel(_timer);
[selfBlock doMethod];
});
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
dispatch_resume(_timer);
}
}
- (void)doMethod
{
NSLog(@"doMethod");
__weak MyObject *selfBlock = self;
[_queue cancelAllOperations];
[_queue addOperationWithBlock:^{
[selfBlock scheduleTimer];
}];
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
MyObject *obj = [MyObject new];
[obj doMethod];
sleep(10);
obj = nil;
NSLog(@"something still points to obj");
sleep(10);
}
return 0;
}
答案 0 :(得分:3)
这里实际上没有保留周期。问题是你正在做的事情或dispatch_release()
的内部(我没有花时间去解决)是发送autorelease
消息而不是release
消息因此,在release
块关闭之后,最终的autorelease
才会发生。如果您将main
例程更改为以下内容,它会向您展示按预期工作的内容:
int main(int argc, const char * argv[])
{
@autoreleasepool {
MyObject *obj = nil;
@autoreleasepool {
obj = [MyObject new];
[obj doMethod];
sleep(10);
NSLog(@"set to nil");
obj = nil;
}
sleep(1); // need this to give the background thread a chance to log
NSLog(@"something still points to obj?");
sleep(10);
NSLog(@"done sleeping");
}
return 0;
}
我更改了你的其他代码以添加一些日志记录并清理了一些东西,但是注释掉了那些仅适用于挑剔清洁编码的更改:)它仍然可以正常工作。
#import <Foundation/Foundation.h>
@interface MyObject : NSObject
@end
@implementation MyObject
{
NSOperationQueue *_queue;
dispatch_source_t _timer;
}
- (id)init
{
self = [super init];
if (self)
{
_queue = [[NSOperationQueue alloc] init];
[_queue setMaxConcurrentOperationCount:1];
}
return self;
}
- (void)dealloc
{
NSLog(@"dealloc");
[_queue cancelAllOperations];
if ( _timer )
{
dispatch_source_cancel(_timer);
dispatch_release(_timer);
// _timer = nil;
}
}
- (void)scheduleTimer
{
NSLog(@"Schedule timer");
if (_timer)
{
dispatch_source_cancel(_timer);
dispatch_release(_timer);
// _timer = nil;
}
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0,
0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
if (_timer)
{
__weak MyObject *selfBlock = self;
dispatch_source_set_event_handler(_timer, ^{
dispatch_source_cancel(_timer);
[selfBlock doMethod];
});
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
dispatch_resume(_timer);
}
}
- (void)doMethod
{
NSLog(@"doMethod");
__weak MyObject *selfBlock = self;
[_queue cancelAllOperations];
[_queue addOperationWithBlock:^{
[selfBlock scheduleTimer];
}];
}
@end
这是我得到的输出:
2013-03-10 18:15:33.829 testtimer[35328:403] doMethod
2013-03-10 18:15:33.832 testtimer[35328:1e03] Schedule timer
2013-03-10 18:15:34.833 testtimer[35328:1e03] doMethod
2013-03-10 18:15:34.835 testtimer[35328:2203] Schedule timer
2013-03-10 18:15:35.837 testtimer[35328:1e03] doMethod
2013-03-10 18:15:35.839 testtimer[35328:1d03] Schedule timer
2013-03-10 18:15:36.839 testtimer[35328:1d03] doMethod
2013-03-10 18:15:36.841 testtimer[35328:1e03] Schedule timer
2013-03-10 18:15:37.842 testtimer[35328:1e03] doMethod
2013-03-10 18:15:37.844 testtimer[35328:1e03] Schedule timer
2013-03-10 18:15:38.846 testtimer[35328:1e03] doMethod
2013-03-10 18:15:38.848 testtimer[35328:1e03] Schedule timer
2013-03-10 18:15:39.849 testtimer[35328:1e03] doMethod
2013-03-10 18:15:39.851 testtimer[35328:1d03] Schedule timer
2013-03-10 18:15:40.851 testtimer[35328:1d03] doMethod
2013-03-10 18:15:40.853 testtimer[35328:2203] Schedule timer
2013-03-10 18:15:41.854 testtimer[35328:2203] doMethod
2013-03-10 18:15:41.856 testtimer[35328:1e03] Schedule timer
2013-03-10 18:15:42.857 testtimer[35328:1d03] doMethod
2013-03-10 18:15:42.859 testtimer[35328:1d03] Schedule timer
2013-03-10 18:15:43.831 testtimer[35328:403] set to nil
2013-03-10 18:15:43.861 testtimer[35328:1d03] doMethod
2013-03-10 18:15:43.861 testtimer[35328:1d03] dealloc
2013-03-10 18:15:44.833 testtimer[35328:403] something still points to obj?
如果您取消sleep(1);
电话,您会看到&#34;某些内容仍指向obj?&#34;日志发生在最后doMethod
&amp; dealloc
日志语句。我怀疑这只是线程和NSLog缓冲,这就是为什么我放入sleep(1);
并确定行为改变了我预期的。
同样来自Xcode文档查看器中dispatch_queue_create
的文档,它说:
提交到队列的任何挂起块都保存对该队列的引用,因此在所有挂起块完成之前不会释放队列。
这有意义,也可能影响各种释放行动的时间。