1

シリアル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;
}
4

1 に答える 1

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);と、「何かがまだ obj を指していますか?」というメッセージが表示されます。log は、最後のdoMethod& dealloclog ステートメントの前に発生します。これは単なるスレッド化と NSLog バッファリングではないかと疑ったため、 を挿入したところ、sleep(1);期待どおりに動作が変化したことを確認しました。

またdispatch_queue_create、Xcodeドキュメントビューアのドキュメントからは、次のように書かれています:

キューに送信された保留中のブロックは、そのキューへの参照を保持するため、すべての保留中のブロックが完了するまで、キューの割り当ては解除されません。

これは理にかなっており、さまざまな割り当て解除アクションのタイミングにも影響を与える可能性があります。

于 2013-03-11T01:21:16.663 に答える