在Objective-C中实现Go语言中的‘defer’语句?

huangapple go评论89阅读模式
英文:

Implementing the ‘defer’ statement from Go in Objective-C?

问题

今天我读到了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"));
    …
}

自动释放对象似乎是唯一一个能够持续到函数结束的解决方案,因为其他解决方案会在当前作用域结束时触发。另一方面,它们可能会在内存中停留更长时间,这可能会引发问题。

调度终结器是我最初的想法,因为块在堆栈上存在,所以我可以很容易地在堆栈展开时执行某些操作。但是在查看文档后,似乎我不能将一个简单的“析构函数”附加到一个块上,对吗?

C++析构函数也是类似的,我会创建一个基于堆栈的对象,并在析构函数运行时执行一个块。这将有一个不好看的缺点,即将普通的.m文件转换为Objective-C++。

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

英文:

Today I read about the defer statement in the Go language:

> A defer statement pushes a function
> call onto a list. The list of saved
> calls is executed after the
> surrounding function returns. Defer is
> commonly used to simplify functions
> that perform various clean-up actions.

I thought it would be fun to implement something like this in Objective-C. Do you have some idea how to do it? I thought about dispatch finalizers, autoreleased objects and C++ destructors.

Autoreleased objects:

@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 objects seem like the only solution that would last at least to the end of the function, as the other solutions would trigger when the current scope ends. On the other hand they could stay in the memory much longer, which would be asking for trouble.

Dispatch finalizers were my first thought, because blocks live on the stack and therefore I could easily make something execute when the stack unrolls. But after a peek in the documentation it doesn’t look like I can attach a simple “destructor” function to a block, can I?

C++ destructors are about the same thing, I would create a stack-based object with a block to be executed when the destructor runs. This would have the ugly disadvantage of turning the plain .m files into Objective-C++?

I don’t really think about using this stuff in production, I’m just interested in various solutions. Can you come up with something working, without obvious disadvantages? Both scope-based and function-based solutions would be interesting.

答案1

得分: 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;
}

应该打印出:

<pre>
begin foo:2
begin foo:1
begin foo:0
end foo:0
end foo:1
end foo:2
begin foo:1
begin foo:0
end foo:0
end foo:1
begin foo:0
end foo:0
begin foo:-1
end foo:-1
0! == 1
1! == 1
2! == 2
releasing 0x100116500.
0x100116500 has been released.
2011-02-05 23:06:21.192 a.out[51141:903] Cannot call factorial on negative numbers
</pre>

英文:

If you could use C++, check Boost's Scope Exit library.


If you don't mind having to type 2 extra words in the beginning and the end of the function, you could use the @finally block.

#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__;})])

Example use:

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

@implementation XXObject
-(int)factorial:(int)x { SCOPE
    
    printf(&quot;begin foo:%d\n&quot;, x);
    DEFER( printf(&quot;end foo:%d\n&quot;, x) );
    
    if (x &gt; 0)
        return x * [self factorial:x-1];
    else if (x == 0)
        return 1;
    else {
        @throw [NSException exceptionWithName:@&quot;NegativeFactorialException&quot;
                                       reason:@&quot;Cannot call factorial on negative numbers&quot;
                                     userInfo:nil];
        return 0;
    }
    
END_SCOPE }

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




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


    int i;
    for (i = 2; i &gt;= -1; -- i) {
        // use DEFER_COPY to retain the local variable &#39;i&#39; and &#39;fact&#39;
        int fact = [x factorial:i];
        DEFER_COPY( printf(&quot;%d! == %d\n&quot;, 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(@&quot;%@&quot;, e);
    }
    [pool drain];
    return 0;
}

Which should print:

<pre>
begin foo:2
begin foo:1
begin foo:0
end foo:0
end foo:1
end foo:2
begin foo:1
begin foo:0
end foo:0
end foo:1
begin foo:0
end foo:0
begin foo:-1
end foo:-1
0! == 1
1! == 1
2! == 2
releasing 0x100116500.
0x100116500 has been released.
2011-02-05 23:06:21.192 a.out[51141:903] Cannot call factorial on negative numbers
</pre>

答案2

得分: 1

阅读Mike Ash在Objective-C中的生成器上的帖子。

英文:

Read Mike Ash's post on generator's in Objective-C.

huangapple
  • 本文由 发表于 2011年2月3日 16:17:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/4883760.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定