实施“延迟” Objective-C 中的 Go 语句?

发布于 2024-10-16 04:53:34 字数 896 浏览 12 评论 0原文

今天读到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++ 会有一个丑陋的缺点吗?

我并没有真正考虑在生产中使用这些东西,我只是对各种解决方案感兴趣。你能想出一些有效且没有明显缺点的方法吗?基于范围和基于功能的解决方案都会很有趣。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冷夜 2024-10-23 04:53:34

如果您可以使用 C++,请检查 Boost 的 作用域退出库。


如果您不介意在函数的开头和结尾输入 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;
}

应该打印:

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

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("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;
}

Which should print:

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
各自安好 2024-10-23 04:53:34

阅读 Mike Ash 关于 生成器的帖子Objective-C

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文