有一个单例类,所谓的RequestManager,它将处理由我的应用程序的不同模块和后台任务发出的请求。
@interface RequestFactory : NSObject
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender;
...
@end
然后我得到另一个类,所谓的SessionDelegate,它将处理请求期间的所有回调。
@interface SessionDelegate : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
@property (weak, nonatomic) id <RequestFactoryDelegate> delegate;
@end
我的想法是将函数封装在这些类中以不重载我的类,因为我需要很多辅助类和CommonCrypto等等。
所以我设置了快速编码协议RequestFactoryDelegate,将收到的数据发送给发起原始请求的发件人。
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender
{
self.sessionDelegate.delegate = sender;
NSMutableURLRequest *request = //create the request here
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithRequest:request];
[dataTask resume];
}
好吧,如果我有一个对象,它就可以了,我们称之为senderA发送请求,因为set delegate总是senderA本身。
问题出现在另一个对象上,例如senderB发送请求 - 甚至不是在同一时间 - 但在senderA发送后不久。
- (void)foo
{
[requestFactory requestDataWith:token
id:id
sender:senderA]; // let's assume this takes 20s
[requestFactory requestDataWith:token
id:id
sender:senderB]; // let's assume this takes 1s
}
因为senderA的请求仍在进行中,所以senderB将委托设置给他,发生的事情是senderB的委托函数运行两次。
<senderB>
<senderB>
嗯......我真的需要实现一个自己的自定义委托(无论是否与RequestFactory在同一个类中),但是如何处理回调方法以便我可以正确响应senderA或senderB?
我的最后一个想法是覆盖NSURLSessionTasks类并实现自己的委托属性或块属性或其他。
非常感谢提前。
答案 0 :(得分:10)
您可以将任意对象附加到任务:
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
id whatever = // anything at all!
[NSURLProtocol setProperty:whatever forKey:@"thing" inRequest:req];
NSURLSessionDownloadTask* task = [[self session] dataTaskWithRequest:req];
稍后(在委托中)检索它,如下所示:
NSURLRequest* req = task.originalRequest;
id thing = [NSURLProtocol propertyForKey:@"thing" inRequest:req];
此处的值(whatever
)可以是任何对象。它可以是一个完成处理程序回调 - 我已经完成了这个并且它工作正常。
答案 1 :(得分:5)
在Objective-C中,有一个子类化的替代方法可能是你想要的:associating objects。
它的工作方式如下:您可以使用自定义键将对象“附加”(关联)到另一个对象,然后再检索它。所以在你的情况下,你会做类似的事情:
#include <objc/runtime.h>
// Top level of your .m file. The type and content of this
// variable don't matter much, we need the _address_ of it.
// See the first link of this answer for details.
static char kDelegateKey = 'd';
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender
{
NSMutableURLRequest *request = //create the request here
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithRequest:request];
// Associate the sender with the dataTask. We use "assign" here
// to avoid retain cycles as per the delegate pattern in Obj-C.
objc_setAssociatedObject(dataTask, &kDelegateKey, sender, OBJC_ASSOCIATION_ASSIGN);
[dataTask resume];
}
- (void)someOtherMethodWithDataTask:(NSURLSessionDataTask *)dataTask
{
// Read the attached delegate.
id<RequestFactoryDelegate> delegate = objc_getAssociatedObject(dataTask, &kDelegateKey);
// Do something with the delegate.
}
答案 2 :(得分:0)
这是我的解决方案。
我只是使用每个sessionTask对象的唯一标识符。所以我的委托对象包含一个字典,其中的块作为成功/失败时执行的值,标识符作为标识正确执行块的键。
在.h文件中,我声明了字典和添加键/值对象的方法:
@property (nonatomic, strong) NSDictionary *completionHandlerDictionary;
- (void)addCompletionHandler:(CompletionHandlerType)handler
forTask:(NSString *)identifier;
在.m文件中,我调用了处理程序块。
- (void)addCompletionHandler:(CompletionHandlerType)handler
forTask:(NSString*)identifier
{
if ([self.completionHandlerDictionary objectForKey:identifier]) {
NSLog(@"Error: Got multiple handlers for a single task identifier. This should not happen.\n");
}
[self.completionHandlerDictionary setObject:handler forKey:identifier];
}
- (void)callCompletionHandlerForTask:(NSString *)identifier
{
CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey:identifier];
if (handler) {
[self.completionHandlerDictionary removeObjectForKey: identifier];
NSLog(@"Calling completion handler.\n");
handler();
}
}
就是这样,很简单。