Реализуете ли вы оператор '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] Невозможно вызвать факториал для отрицательных чисел