ios – 将NSURLConnection切换到NSURLSession后,自定义NSURLProtocol变慢

我有一个自定义NSURLProtocol(“UrlProtocol”),用于在导航到特定网站时拦截来自UIWebView的请求,并在发送之前应用额外的HTTP标头.我跟着 https://www.raywenderlich.com/59982/nsurlprotocol-tutorial上班了.我的问题是从已弃用的NSURLConnection切换到NSURLSession:我测试了一个非常简单的单文件html页面,它已成功加载.但是,使用js文件,图像等资源的稍微复杂的站点将会超时,而使用NSURLConnection时,整个站点将在几秒钟内加载.

我将使用NSURLConnection粘贴原始UrlProtocol,然后使用NSURLSession粘贴新类.原本的:

#import "UrlProtocol.h"
#import "Globals.h"

@implementation UrlProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    if (![request.URL.absoluteString hasPrefix:@"http"]) return NO; //No need to intercept non-http requests

    if ([NSURLProtocol propertyForKey:@"handled" inRequest:request]) {
        return NO;
    }

    return YES;
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    NSString* key = @"custom-auth-header";
    Globals* globals = [Globals getInstance];
    NSString* token = [globals token];

    NSMutableURLRequest *newRequest = [request mutableCopy]; //Create a mutable copy that can be modified
    [newRequest setValue:token forHTTPHeaderField:key];

    return [newRequest copy]; //return a non-mutable copy
}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
    return [super requestIsCacheEquivalent:a toRequest:b];
}

- (void)startLoading {
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"handled" inRequest:newRequest];

    self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}

- (void)stopLoading {
    NSLog(@"stopLoading");
    [self.connection cancel];
    self.connection = nil;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self.client URLProtocol:self didFailWithError:error];
}

@end

针对每个请求使用NSURLSessionDataTasks的新UrlProtocol:

#import "UrlProtocol.h"
#import "Globals.h"

@implementation UrlProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest * _Nonnull) request {
    if (![request.URL.absoluteString hasPrefix:@"http"]) return NO; //No need to intercept non-http requests

    if ([NSURLProtocol propertyForKey:@"handled" inRequest:request]) {
        return NO;
    }

    return YES;
}

+ (NSURLRequest * _Nonnull)canonicalRequestForRequest:(NSURLRequest * _Nonnull)request {
    NSString* key = @"custom-auth-header";
    Globals* globals = [Globals getInstance];
    NSString* token = [globals token];

    NSMutableURLRequest *newRequest = [request mutableCopy]; //Create a mutable copy that can be modified
    [newRequest setValue:token forHTTPHeaderField:key];
    return [newRequest copy]; //return a non-mutable copy
}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest * _Nonnull)a toRequest:(NSURLRequest * _Nonnull)b {
    return [super requestIsCacheEquivalent:a toRequest:b];
}

- (void)startLoading {
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"handled" inRequest:newRequest];
    [Globals setUrlSessionDelegate:self];
    Globals* globals = [Globals getInstance];
    self.dataTask = [globals.session dataTaskWithRequest:newRequest];
    [self.dataTask resume];
}

- (void)URLSession:(NSURLSession * _Nonnull)session dataTask:(NSURLSessionDataTask * _Nullable)dataTask didReceiveData:(NSData * _Nullable)data{
    [self.client URLProtocol:self didLoadData:data];
}

- (void)URLSession:(NSURLSession * _Nonnull)session dataTask:(NSURLSessionDataTask * _Nullable)dataTask didReceiveResponse:(NSURLResponse * _Nullable)response
                 completionHandler:(void (^ _Nullable)(NSURLSessionResponseDisposition))completionHandler{
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession * _Nonnull)session task:(NSURLSessionTask * _Nonnull)task didCompleteWithError:(NSError * _Nullable)error{
    if (error){
        [self.client URLProtocol:self didFailWithError:error];
    } else {
        [self.client URLProtocolDidFinishLoading:self];
    }
}

- (void)URLSession:(NSURLSession * _Nonnull)session task:(NSURLSessionTask * _Nonnull)task willPerformHTTPRedirection:(NSHTTPURLResponse * _Nonnull)response 
                        newRequest:(NSURLRequest * _Nonnull)request completionHandler:(void (^ _Nonnull)(NSURLRequest * _Nullable))completionHandler {
    completionHandler(request);
}

- (void)stopLoading {
    [self.dataTask cancel];
    self.dataTask = nil;
}

@end

“Globals”是一个单例,我已经初始化了一个NSURLSession,意在整个应用程序的运行时使用.它还包含我为所有请求设置为自定义HTTP标头的令牌:

#import "Globals.h"
#import "UrlProtocol.h"

@implementation Globals
@synthesize token;
@synthesize session;

static Globals *instance = nil;

+(Globals*) getInstance
{
    @synchronized(self)
    {
        if (instance == nil)
        {
            instance = [Globals new];
        }
    }
    return instance;
}

//UrlProtocol class has no init method,so the NSURLSession delegate is being set on url load. We will ensure only one NSURLSession is created.
+(void) setUrlSessionDelegate:(UrlProtocol*) urlProtocol{
    Globals* globals = [Globals getInstance];
    if (!globals.session){
        globals.session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.defaultSessionConfiguration  delegate:urlProtocol delegateQueue:nil];
    }
}

@end

解决方法

通过为每个NSURLSessionDataTask创建新的默认NSURLSession解决了我的问题.我试图为我的所有任务分享一个NSURLSession的方式出了点问题. URLProtocol的startLoading方法现在如下:
- (void)startLoading {
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"handled" inRequest:newRequest];
    NSURLSessionConfiguration* config = NSURLSessionConfiguration.defaultSessionConfiguration;
    NSURLSession* session = [NSURLSession sessionWithConfiguration:config  delegate:self delegateQueue:nil];
    self.dataTask = [session dataTaskWithRequest:newRequest];
    [self.dataTask resume];
}

之前的简单HTML页面测试必须有效,因为加载页面只需要一个任务

相关文章

背景 前端时间产品经理决定使用百度统计,使得 工程B 中原统计sdk-友盟统计,需要被去除。之前尝试去除...
结论: alloc负责分配内存和创建对象对应的isa指针; init只是返回alloc生成的对象。 所以alloc后,多次...
更新 如果UI愿意把启动图切割成n份,按一定约束在launchscreen.storyboard中进行排版,启动图效果会更好...
最近在看一本书《Effective OC 2.0》,今天看到有个tip是OC适中循环各自优劣性,作者最终推荐此块循环。...
// // ViewController.m // paintCodeTestOC //gif // Created by LongMa on 2019/7/25. // #import &a...
背景介绍 一般情况下,出于省电、权限、合理性等因素考虑,给人的感觉是很多奇怪的需求安卓可以实现,但...