6

今日、私deferは Go 言語のステートメントについて読みました:

deferステートメントは、関数呼び出しをリストにプッシュします。保存された呼び出しのリストは、周囲の関数が戻った後に実行されます。Defer は、さまざまなクリーンアップ アクションを実行する関数を単純化するためによく使用されます。

このようなものを Objective-C で実装するのは楽しいだろうと思いました。それを行う方法について何か考えがありますか?ディスパッチ ファイナライザー、自動解放オブジェクト、および C++ デストラクタについて考えました。

自動解放されたオブジェクト:

@interface Defer : NSObject {}
+ (id) withCode: (dispatch_block_t) block;
@end

@implementation Defer
- (void) dealloc {
    block();
    [super dealloc];
}
@end

#define defer(__x) [Defer withCode:^{__x}]

- (void) function
{
    defer(NSLog(@"Done"));
    …
}

現在のスコープが終了すると他のソリューションがトリガーされるため、自動解放されたオブジェクトは、少なくとも関数の最後まで続く唯一のソリューションのようです。一方で、それらは記憶にずっと長くとどまる可能性があり、これは問題を引き起こします.

ブロックはスタック上に存在するため、スタックがアンロールされたときに何かを簡単に実行できるため、ディスパッチ ファイナライザーが最初に考えられました。しかし、ドキュメンテーションをのぞいてみると、単純な「デストラクタ」関数をブロックにアタッチできるようには見えませんよね?

C++ デストラクタもほぼ同じです。デストラクタの実行時に実行されるブロックを含むスタック ベースのオブジェクトを作成します。.mこれには、プレーンファイルを Objective-C++ に変換するという醜い欠点がありますか?

このようなものを本番環境で使用することについてはあまり考えていません。さまざまなソリューションに興味があるだけです。明らかな欠点なしで、機能するものを思いつくことができますか? 範囲ベースのソリューションと機能ベースのソリューションの両方が興味深いでしょう。

4

2 に答える 2

2

C++ を使用できる場合は、Boost のScope Exitライブラリを確認してください。


関数の最初と最後に 2 つの余分な単語を入力しなくてもよい場合は、@finallyブロックを使用できます。

#define SCOPE               {id _defered_actions__=[[NSMutableArray alloc]init];@try{
#define END_SCOPE           }@finally{for(void(^action)()in[_defered_actions__ reverseObjectEnumerator])action();[_defered_actions__ release];}}
#define DEFER_COPY(_code__) {id _blk__=[^{_code__;}copy];[_defered_actions__ addObject:_blk__];[_blk__ release];}
#define DEFER(_code__)      ([_defered_actions__ addObject:(^{_code__;})])

使用例:

@interface XXObject : NSObject {
}
-(int)factorial:(int)x;
@end

@implementation XXObject
-(int)factorial:(int)x { SCOPE

    printf("begin foo:%d\n", x);
    DEFER( printf("end foo:%d\n", x) );

    if (x > 0)
        return x * [self factorial:x-1];
    else if (x == 0)
        return 1;
    else {
        @throw [NSException exceptionWithName:@"NegativeFactorialException"
                                       reason:@"Cannot call factorial on negative numbers"
                                     userInfo:nil];
        return 0;
    }

END_SCOPE }

-(void)dealloc {
    printf("%p has been released.\n", self);
    [super dealloc];
}
@end




void do_stuff() { SCOPE

    __block XXObject* x = [[XXObject alloc] init];
    DEFER({
        printf("releasing %p.\n", x);
        [x release];
    });


    int i;
    for (i = 2; i >= -1; -- i) {
        // use DEFER_COPY to retain the local variable 'i' and 'fact'
        int fact = [x factorial:i];
        DEFER_COPY( printf("%d! == %d\n", i, fact) );
    }

END_SCOPE }




int main () {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    @try {
        do_stuff();
    } @catch(NSException* e) {
        // note that the @finally statements might not be called in 64-bit if we
        // left the exception uncaught.
        NSLog(@"%@", e);
    }
    [pool drain];
    return 0;
}

どちらを印刷する必要がありますか:

開始 foo:2
開始 foo:1
開始 foo:0
終了 foo:0
終了 foo:1
終了 foo:2
開始 foo:1
開始 foo:0
終了 foo:0
終了 foo:1
開始 foo:0
終了 foo:0
開始 foo:-1
終了 foo:-1
0! == 1
1!== 1
2!== 2
0x100116500 を解放します。
0x100116500 が公開されました。
2011-02-05 23:06:21.192 a.out[51141:903] 負の数で階乗を呼び出すことはできません
于 2011-02-05T15:20:57.750 に答える
1

Objective-C のジェネレーターに関する Mike Ash の投稿を読んでください。

于 2011-02-03T08:57:02.337 に答える