Objective c NSURLSession委派:如何相应地实现自定义SessionLegate类?

Objective c NSURLSession委派:如何相应地实现自定义SessionLegate类?,objective-c,ios7,nsurlrequest,delegation,nsurlsession,Objective C,Ios7,Nsurlrequest,Delegation,Nsurlsession,获得了一个名为RequestManager的单例类,它将处理不同模块发出的请求和我的应用程序的后台任务 @interface RequestFactory : NSObject - (void)requestDataWith:(NSString *)token id:(NSString *)id sender:(id<RequestFactoryDelegate>)sender; ... @end 由于

获得了一个名为RequestManager的单例类,它将处理不同模块发出的请求和我的应用程序的后台任务

@interface RequestFactory : NSObject

- (void)requestDataWith:(NSString *)token
                     id:(NSString *)id
                 sender:(id<RequestFactoryDelegate>)sender;
...

@end
由于senderA的请求仍在进行中,senderB将委托设置给他,所发生的是senderB的委托函数运行了两次

 <senderB>
 <senderB>

嗯。。。我确实需要实现自己的自定义委托(无论是否与RequestFactory在同一个类中),但是如何处理回调方法,以便能够正确响应senderA或senderB

我的最后一个想法是重写NSURLSessionTasks类,并实现自己的委托属性或块属性或其他属性


非常感谢。

在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.
}
#包括
//.m文件的顶层。本报告的类型和内容
//变量并不重要,我们需要它的地址。
//有关详细信息,请参阅此答案的第一个链接。
静态字符kDelegateKey='d';
-(void)requestDataWith:(NSString*)令牌
id:(NSString*)id
发件人:(id)发件人
{
NSMutableURLRequest*请求=//在此处创建请求
NSURLSessionDataTask*dataTask=[self.defaultSessionDataTaskWithRequest:request];
//将发送者与dataTask关联。我们在此处使用“分配”
//避免按照Obj-C中的委托模式保留周期。
objc_setAssociatedObject(dataTask和kDelegateKey、发送方、objc_ASSOCIATION_ASSIGN);
[数据任务恢复];
}
-(void)someOtherMethodWithDataTask:(NSURLSessionDataTask*)dataTask
{
//阅读附件中的学员。
id delegate=objc_getAssociatedObject(dataTask和kDelegateKey);
//对代表做点什么。
}
这是我的解决方案

我只是使用每个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();
    }
}

就这么简单。

您可以将任意对象附加到任务:

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];

此处的值(
无论什么
)可以是任何对象。它可以是一个完成处理程序回调-我已经完成了,而且效果很好。

非常感谢,慕尼黑人的正确回答;)顺便问一下,还有其他方便的方法来处理来自委托的回调吗?好吧,除了使用接口,您还可以使用块。Objective-C允许您像对待对象一样对待块,因此您可以这样做:
objc_setAssociatedObject(foo,&kKey,aBlock,objc_ASSOCIATION_COPY);MyBlockType block=objc_getAssocatedObject(foo,&kKey);块(条)嗯,您的方法不起作用-我刚刚实现了它,但是getAssociatedObject函数的返回值为零。此外,我的意思是,有没有其他模式可以用来实现NSURLSession?例如,我在类的文档中没有找到任何关于自定义委托的内容……好吧,我发现关联的对象被释放,并且从另一个类调用它时变为nil-logical。最终找到了问题的解决方案,非常简单。您知道调用finishtasks和cancel方法的最佳位置吗?我的意思是,在创建请求的函数结束时调用它是没有意义的。。。只要requestFactory是每种实例网络的单例:/I我认为有必要澄清的是,
NSURLSession
对象的单委托必须处理所有任务。此外,
NSURLSessionTask
s不能被子类化,也没有这样的“用户上下文”属性。嗯,这是一个设计缺陷——但唉,我不是苹果。你的设计需要考虑这一点。TBH我很喜欢NSLLIST的设计模式,因为我知道如何正确处理它。所有任务使用一个会话对象的想法真的很酷,你可以通过查看WWDC2013视频来了解其优点。那你为什么要问?无论如何,问题不在于有一个会话和一个或多个会话任务。这实际上是一个很好的分离。我的意思是你的问题出在哪里。你可以用几种方法来解决这个问题。使用字典作为一个可行的解决方案,但我认为它是一个“黑客”,由于API的局限性。尽管如此,我也这么做;)@CouchDeveloper实际上,您可以通过将任意属性值附加到NSURLSessionTask的NSURLRequest来向其添加“用户上下文”。@matt有趣的解决方案,谢谢您提供的信息!也可以使用
task.tasksdescription
property@JohnSmith如果不是NSString,则不会。在我的回答中,我给出了附加一个完成处理程序(一个块)的示例-你不能仅仅用
tasksdescription
来实现这一点。我回答的要点是,它是一般性的。是的,当然,我指出,对于未来的读者来说,这是一个更简单的示例,用于获取附加到任务的属性。谢谢你的回答,我以前不知道。@JohnSmith,但这与最初提问者的问题(以及我的回答)无关。
taskDescription
的存在是显而易见的,但它与本页的内容无关。ok。顺便说一下,我刚刚验证了当应用程序重新连接会话时,不会记住额外的属性。
- (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();
    }
}
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];