看起来你想要与一个旨在接受代理对象的现有类通信。有许多方法,包括:
- 使用分类添加基于块的适当方法变体;
- 使用派生类添加基于块的变体;和
- 编写实现协议并调用您的块的类。
这里是一种实现(3)的方法。首先,假设你的SomeObject是:
@protocol SomeObjectDelegate
@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
@interface SomeObject : NSObject
{
}
+ (void) testCallback:(id<SomeObjectDelegate>)delegate;
@end
@implementation SomeObject
+ (void) testCallback:(id<SomeObjectDelegate>)delegate
{
[delegate stuffDone:[NSNumber numberWithInt:42]];
[delegate stuffFailed];
}
@end
我们有一些测试方法 - 你将拥有一个真实的SomeObject。
现在定义一个实现协议并调用你提供的代码块的类:
#import "SomeObject.h"
typedef void (^StuffDoneBlock)(id anObject);
typedef void (^StuffFailedBlock)();
@interface SomeObjectBlockDelegate : NSObject<SomeObjectDelegate>
{
StuffDoneBlock stuffDoneCallback;
StuffFailedBlock stuffFailedCallback;
}
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
- (void)dealloc;
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
这个类保存你传入的块,并响应协议回调时调用它们。实现很简单:
@implementation SomeObjectBlockDelegate
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
if (self = [super init])
{
stuffDoneCallback = Block_copy(done);
stuffFailedCallback = Block_copy(fail);
}
return self;
}
- (void)dealloc
{
Block_release(stuffDoneCallback);
Block_release(stuffFailedCallback);
[super dealloc];
}
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
return (SomeObjectBlockDelegate *)[[[SomeObjectBlockDelegate alloc] initWithOnDone:done andOnFail:fail] autorelease];
}
- (void)stuffDone:(id)anObject
{
stuffDoneCallback(anObject);
}
- (void)stuffFailed
{
stuffFailedCallback();
}
@end
你需要记住的唯一一件事是在初始化时使用Block_copy()对块进行复制,并稍后使用Block_release()释放它们 - 这是因为块是堆栈分配的,你的对象可能会比创建堆栈帧更长寿;Block_copy()将在堆中创建一个副本。
现在,你可以调用委托方法并传递块:
[SomeObject testCallback:[SomeObjectBlockDelegate
someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(@"Done: %@", anObject); }
andOnFail:^{ NSLog(@"Failed"); }
]
];
你可以使用这种技术来为任何协议包装块。
ARC 补充说明
针对评论所述:要使其与 ARC 兼容,只需删除对
Block_copy()
的调用,直接进行赋值即可。
stuffDoneCallback = done
stuffFailedCallback = fail
同时移除dealloc
方法。你也可以将Blockcopy
改为copy
,即stuffDoneCallback = [done copy];
,并且这可能是从阅读ARC文档中所需的。然而,由于赋值给了一个强引用变量,导致ARC保留了赋值的值,并且保留堆栈块会将其复制到堆上。因此,生成的ARC代码无论是否使用copy
都会产生相同的结果。