如何简化回调逻辑与块? [英] How to simplify callback logic with a Block?
问题描述
假设我需要与一个提供协议的类进行通信,并在操作完成时调用委托方法,如下所示:
@protocol SomeObjectDelegate
现在,我已经决定,虽然我可以让另一个类实现
@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
@interface SomeObject:NSObject
{
}
@end
stuffDone:
delegate方法,我决定,我宁愿将进程封装在一个块,它写在接近SomeObject
被实例化,调用等。我该怎么做呢?或者换句话说,如果您查看这篇有关块的着名文章(在替换回调部分);我如何在SomeObject中编写一个方法接受completionHandler: $ c $>
解决方案听起来你想与一个现有的类进行通信,这个类被设计为接受一个委托对象。有多种方法,包括:
- 使用类别添加适当方法的基于块的变体;
- 使用派生类添加基于块的变体;
下面是一个实现协议并调用你的块的类。方法做(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 stuffDone:[NSNumber numberWithInt:42]];
[delegate stuffFailed];
}
@end
测试 - 你将有一个真正的SomeObject。
现在定义一个实现协议并调用你提供的块的类:
#importSomeObject.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 *)someObjectBlockDelegate *)someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock) 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] code>,这是你可能认为是阅读ARC文档需要的。然而,它不是因为赋值是一个强变量,导致ARC保留分配的值 - 并保留一个堆栈块将其复制到堆。因此,生成的ARC代码产生相同的结果,有或没有
copy
。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
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 whereSomeObject
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 acompletionHandler:
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:
- using a category to add block-based variants of the appropriate methods;
- use a derived class to add the block-based variants; and
- write a class which implements the protocol and calls your blocks.
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
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
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 Addendum
In response to the comment: to make this ARC compatible just remove the calls to
Block_copy()
leaving direct assignments:stuffDoneCallback = done; stuffFailedCallback = fail;
and remove the
dealloc
method. You can also changeBlockcopy
tocopy
, 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 thecopy
.这篇关于如何简化回调逻辑与块?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!