3

有一个单例类,称为 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 等的辅助类。

所以我设置快速编码一个协议 RequestFactoryDe​​legate 将接收到的数据发送给发起原始请求的发送者。

- (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,因为设置的委托始终是 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 类并实现自己的委托属性或块属性或其他任何东西。

提前谢谢了。

4

3 回答 3

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) 可以是任何对象。它可以是一个完成处理程序回调——我已经完成了,它工作得很好。

于 2013-11-14T17:48:04.567 回答
5

在 Objective-C 中,有一个替代子类的方法,这可能是您想要的:关联对象

它的工作原理如下:您可以使用自定义键将一个对象“附加”(关联)到另一个对象,然后再检索它。所以在你的情况下,你会做这样的事情:

#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.
}
于 2013-09-24T21:46:55.660 回答
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();
    }
}

就是这样,就这么简单。

于 2013-09-25T08:50:03.867 回答