Реализуете ли вы оператор 'defer' из Go в Objective-C?

Сегодня я прочитал о 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++?

Я не думаю об использовании этого материала в производстве, я просто заинтересован в различных решениях. Можете ли вы придумать что-нибудь работающее, без явных недостатков? И основанные на сфере действия и основанные на функции решения были бы интересны.

2 ответа

Если вы можете использовать C++, проверьте библиотеку Boost's 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;
}

Который должен напечатать:

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 выпускает 0x100116500.
0x100116500 был выпущен.
2011-02-05 23:06:21.192 a.out[51141:903] Невозможно вызвать факториал для отрицательных чисел

Прочитайте пост Майка Эша о генераторе в Objective-C.

Другие вопросы по тегам