在Objective-C中实现Go语言中的“defer”语句?

6
今天我读到了Go语言中的defer语句:

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"));
    …
}

Autoreleased对象似乎是唯一的解决方案,可以持续到函数的末尾,因为其他解决方案会在当前作用域结束时触发。另一方面,它们可能会停留在内存中更长时间,这可能会带来麻烦。

调度finalizer是我首先想到的,因为块位于堆栈上,因此当堆栈取消时,我可以轻松地执行某些操作。但是在查看文档后,似乎我不能将简单的“析构函数”函数附加到一个块上,对吗?

C++析构函数大致相同,我会创建一个基于堆栈的对象,并使用一个块来在析构函数运行时执行。这将有一个丑陋的缺点,即将普通的.m文件转换为Objective-C++?

我并不真的考虑在生产中使用这些东西,我只是对各种解决方案感兴趣。你能想出一些工作的方法,没有明显的缺点吗?基于作用域和基于函数的解决方案都很有趣。


你的解决方案没有问题。事实上,我在我的博客上也提出了非常类似的想法(http://www.a-coding.com/2014/03/defer-in-objective-c.html)。我还为此编写了单元测试。 - Alejandro
2个回答

2
如果您使用C++,可以尝试使用Boost的Scope Exit库。
如果您不介意在函数的开头和结尾多打两个单词,您可以使用@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] 不能对负数调用阶乘。 

1

这是一段相当复杂的代码需要消化 :) 如果我理解正确,这正是自动释放的解决方案? - zoul
Sorta;它提供了一个基础,你可以在此基础上实现像defer这样的东西。你可以将其附加到自动释放池中,但这样做存在一定风险,因为自动释放池可能会嵌套等等... 你也可以混合使用C ++并使用析构函数来触发该块。 - bbum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接