如何简化回调逻辑与块? [英] How to simplify callback logic with a Block?

查看:87
本文介绍了如何简化回调逻辑与块?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我需要与一个提供协议的类进行通信,并在操作完成时调用委托方法,如下所示:

  @protocol SomeObjectDelegate 

@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;

@end

@interface SomeObject:NSObject
{
}
@end

现在,我已经决定,虽然我可以让另一个类实现 stuffDone: delegate方法,我决定,我宁愿将进程封装在一个块,它写在接近 SomeObject 被实例化,调用等。我该怎么做呢?或者换句话说,如果您查看这篇有关块的着名文章(在替换回调部分);我如何在SomeObject中编写一个方法接受 completionHandler:

$ c $>

解决方案

听起来你想与一个现有的类进行通信,这个类被设计为接受一个委托对象。有多种方法,包括:


  1. 使用类别添加适当方法的基于块的变体;

  2. 使用派生类添加基于块的变体;

下面是一个实现协议并调用你的块的类。方法做(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 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:

  1. using a category to add block-based variants of the appropriate methods;
  2. use a derived class to add the block-based variants; and
  3. 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 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.

这篇关于如何简化回调逻辑与块?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆