【发布时间】:2011-12-27 15:46:54
【问题描述】:
我正在寻找一个类别来用回调块替换委托方法,用于许多简单的 iOS API。类似于 NSURLConnection 上的 sendAsyc 块。有 2 种技术是无泄漏的,并且似乎工作正常。每个的优点/缺点是什么?有没有更好的办法?
选项 1. 使用类别在 NSObject 上实现委托的回调方法,外部回调块作用域。
// Add category on NSObject to respond to the delegate
@interface NSObject(BlocksDelegate)
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
@end
@implementation NSObject(BlocksDelegate)
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// Self is scoped to the block that was copied
void(^callback)(NSInteger) = (id)self;
// Call the callback passed if
callback(buttonIndex);
[self release];
}
@end
// Alert View Category
@implementation UIAlertView (BlocksDelegate)
+ (id) alertWithTitle:(NSString*)title
message:(NSString*)message
clickedBlock:(void(^)(NSInteger))buttonIndexClickedBlock
cancelButtonTitle:(NSString*)cancelButtonTitle
otherButtonTitles:(NSString*)otherButtonTitles
{
// Copy block passed in to the Heap and will stay alive with the UIAlertView
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:[buttonIndexClickedBlock copy]
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:otherButtonTitles, nil];
// Display the alert
[alert show];
// Autorelease the alert
return [alert autorelease];
}
@end
这在 NSObject 上添加了很多方法,似乎它可能会导致任何其他尝试使用标准委托方法的类出现问题。但它使块与对象保持活动状态并返回回调而没有我发现的任何泄漏。
选项 2. 创建一个轻量级类来包含该块,将其与该类动态关联,以便它将留在堆中并在回调完成时将其删除。
// Generic Block Delegate
@interface __DelegateBlock:NSObject
typedef void (^HeapBlock)(NSInteger);
@property (nonatomic, copy) HeapBlock callbackBlock;
@end
@implementation __DelegateBlock
@synthesize callbackBlock;
- (id) initWithBlock:(void(^)(NSInteger))callback
{
// Init and copy Callback Block to the heap (@see accessor)
if (self = [super init])
[self setCallbackBlock:callback];
return [self autorelease];
}
- (void) dealloc
{
// Release the block
[callbackBlock release], callbackBlock = nil;
[super dealloc];
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// Return the result to the callback
callbackBlock(buttonIndex);
// Detach the block delegate, will decrement retain count
SEL key = @selector(alertWithTitle:message:clickedBlock:cancelButtonTitle:otherButtonTitles:);
objc_setAssociatedObject(alertView, key, nil, OBJC_ASSOCIATION_RETAIN);
key = nil;
// Release the Alert
[alertView release];
}
@end
@implementation UIAlertView (BlocksDelegate)
+ (id) alertWithTitle:(NSString*)title
message:(NSString*)message
clickedBlock:(void(^)(NSInteger))buttonIndexClickedBlock
cancelButtonTitle:(NSString*)cancelButtonTitle
otherButtonTitles:(NSString*)otherButtonTitles
{
// Create class to hold delegatee and copy block to heap
DelegateBlock *delegatee = [[__DelegateBlock alloc] initWithBlock:buttonIndexClickedBlock];
[[delegatee retain] autorelease];
// Create delegater
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:delegatee
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:otherButtonTitles, nil];
// Attach the Delegate Block class to the Alert View, increase the retain count
objc_setAssociatedObject(alert, _cmd, delegatee, OBJC_ASSOCIATION_RETAIN);
// Display the alert
[alert show];
return alert;
}
@end
我喜欢这不会在 NSObject 之上添加任何东西,而且事情更加分离。它通过函数的地址附加到实例。
【问题讨论】:
-
选项 3:子类
UIAlertView. -
对。子类化工作。但是当我对每个苹果 API 进行子类化以添加一个方法调用时,它会变得杂乱无章并且具有更少的可重用代码。此外,我将所有这些 API 放在一个类中,以便轻松导入和使用类别,使方法调用更清晰,更接近苹果 API。如果最终有一种很好的通用方法来保存和返回块,那么只要我需要向苹果 API 添加一个异步块方法,就可以通过微小的更改重复使用该代码。
-
好的,我明白了。这是一项艰巨的任务。我只是想让你免于在运行时捣乱。
标签: objective-c ios objective-c-blocks