问题描述
假设我需要与一个提供协议并在操作完成时调用委托方法的类进行通信,如下所示:
Let's say I need to communicate with a class that provides a protocol and calls delegate methods when an operation is complete, as so:
@protocol SomeObjectDelegate
@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
@interface SomeObject : NSObject
{
}
@end
现在,我决定虽然我可以让另一个类实现 stuffDone:
委托方法,但我决定我宁愿将过程封装到一个写在靠近 SomeObject
实例化、调用等位置的块.我该怎么做?或者换句话说,如果您查看 this 关于块的著名文章(在替换回调部分);我如何在 SomeObject 中编写一个接受 completionHandler:
类型的方法?
Now, I've decided that while I could make another class implement the stuffDone:
delegate method, I've decided that I'd rather encapsulate the process into a block which is written somewhere close to where SomeObject
is instantiated, called, etc. How might I do this? Or in other words, if you look at this famous article on blocks (in the Replace Callbacks section); how might I write a method in SomeObject that accepts a completionHandler:
of sorts?
推荐答案
听起来您希望与设计为采用委托对象的现有类进行通信.有多种方法,包括:
It sounds like you wish to communicate with an existing class which is designed to take a delegate object. There are a number of approaches, including:
- 使用类别添加适当方法的基于块的变体;
- 使用派生类添加基于块的变体;和
- 编写一个实现协议并调用块的类.
这是执行 (3) 的一种方法.首先让我们假设您的 SomeObject 是:
Here is one way to do (3). First let's assume your SomeObject is:
@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.
so we have some way to test - you will have a real SomeObject.
现在定义一个实现协议并调用您提供的块的类:
Now define a class which implements the protocol and calls your supplied blocks:
#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;
// protocol
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
这个类保存你传入的块并调用它们以响应协议回调.实现很简单:
This class saves the blocks you pass in and calls them in response to the protocol callbacks. The implementation is straightforward:
@implementation SomeObjectBlockDelegate
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
if (self = [super init])
{
// copy blocks onto heap
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];
}
// protocol
- (void)stuffDone:(id)anObject
{
stuffDoneCallback(anObject);
}
- (void)stuffFailed
{
stuffFailedCallback();
}
@end
你唯一需要记住的是在初始化时 Block_copy() 块,然后在 Block_release() 它们 - 这是因为块是堆栈分配的,并且你的对象可能比它创建的堆栈帧寿命更长;Block_copy() 在堆中创建一个副本.
The only thing you need to remember is to Block_copy() the blocks when initializing and to Block_release() them later - this is because blocks are stack allocated and your object may outlive its creating stack frame; Block_copy() creates a copy in the heap.
现在您可以将所有基于委托的方法传递给它块:
Now you can all a delegate-based method passing it blocks:
[SomeObject testCallback:[SomeObjectBlockDelegate
someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(@"Done: %@", anObject); }
andOnFail:^{ NSLog(@"Failed"); }
]
];
您可以使用此技术为任何协议包装块.
You can use this technique to wrap blocks for any protocol.
ARC 附录
回应评论:为了使这个 ARC 兼容,只需删除对 Block_copy()
的调用,留下直接赋值:
In response to the comment: to make this ARC compatible just remove the calls to Block_copy()
leaving direct assignments:
stuffDoneCallback = done;
stuffFailedCallback = fail;
并删除 dealloc
方法.您还可以将 Blockcopy
更改为 copy
,即 stuffDoneCallback = [done copy];
,这可能是您从阅读ARC 文档.然而,这并不是因为分配给一个强变量而导致 ARC 保留分配的值 - 并保留一个堆栈块将其复制到堆中.因此,无论是否使用 copy
,生成的 ARC 代码都会产生相同的结果.
and remove the dealloc
method. You can also change Blockcopy
to copy
, i.e. stuffDoneCallback = [done copy];
, and this is what you might assume is needed from reading the ARC documentation. However it is not as the assignment is to a strong variable which causes ARC to retain the assigned value - and retaining a stack block copies it to the heap. Therefore the ARC code generated produces the same results with or without the copy
.
这篇关于如何使用 Block 简化回调逻辑?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!