究竟是什么+ [NSURLProtocol setProperty:forKey:inRequest:]

时间:2016-03-20 16:11:12

标签: objective-c nsurlrequest nsurlprotocol

NSURLProtocol定义了以下方法:

/*! 
    @method propertyForKey:inRequest:
    @abstract Returns the property in the given request previously
    stored with the given key.
    @discussion The purpose of this method is to provide an interface
    for protocol implementors to access protocol-specific information
    associated with NSURLRequest objects.
    @param key The string to use for the property lookup.
    @param request The request to use for the property lookup.
    @result The property stored with the given key, or nil if no property
    had previously been stored with the given key in the given request.
*/
+ (nullable id)propertyForKey:(NSString *)key inRequest:(NSURLRequest *)request;

/*! 
    @method setProperty:forKey:inRequest:
    @abstract Stores the given property in the given request using the
    given key.
    @discussion The purpose of this method is to provide an interface
    for protocol implementors to customize protocol-specific
    information associated with NSMutableURLRequest objects.
    @param value The property to store. 
    @param key The string to use for the property storage. 
    @param request The request in which to store the property. 
*/
+ (void)setProperty:(id)value forKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;

/*!
    @method removePropertyForKey:inRequest:
    @abstract Remove any property stored under the given key
    @discussion Like setProperty:forKey:inRequest: above, the purpose of this
        method is to give protocol implementors the ability to store 
        protocol-specific information in an NSURLRequest
    @param key The key whose value should be removed
    @param request The request to be modified
*/
+ (void)removePropertyForKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;

但是,如果我尝试将NSURLAuthenticationChallenge与请求关联,请使用以下代码

[NSURLProtocol setProperty:challenge forKey:@"challenge" inRequest:self.request];

我在日志中看到下一个错误:

2016-03-20 18:00:41.252 Web@Work[6084:586155] ERROR: createEncodedCachedResponseAndRequestForXPCTransmission - Invalid protocol-property list - CFURLRequestRef. protoProps=<CFBasicHash 0x7f98ec6dcca0 [0x10684e180]>{type = mutable dict, count = 3,
entries =>
    0 : <CFString 0x101f47938 [0x10684e180]>{contents = "challenge"} = <NSURLAuthenticationChallenge: 0x7f98ec4cd700>
    1 : <CFString 0x7f98e9fd03a0 [0x10684e180]>{contents = "Accept-Encoding"} = <CFBasicHash 0x7f98ec7c4490 [0x10684e180]>{type = mutable dict, count = 1,
entries =>
    2 : <CFString 0x7f98ec78b930 [0x10684e180]>{contents = "Accept-Encoding"} = <CFString 0x106330828 [0x10684e180]>{contents = "gzip, deflate"}
}

    2 : <CFString 0x1063310e8 [0x10684e180]>{contents = "kCFURLRequestAllowAllPOSTCaching"} = <CFBoolean 0x10684ebf0 [0x10684e180]>{value = true}
}

从这条消息中,我怀疑只有属性列表支持的对象可能会使用此API与NSURLRequest相关联。但我不完全理解为什么。

另外,我的问题是应该使用这个API。如何正确使用?我想知道setProperty:forKey:inRequest:究竟是做什么的,因为它似乎做了更多工作,而不仅仅是将对象与请求相关联。

UPD 因此,最小的可验证示例。非常简单的NSURLProtocol子类:

#import "MyURLProtocol.h"

#define kOurRecursiveRequestFlagProperty @"recursive"

@interface MyURLProtocol ()
@property NSURLConnection *connection;
@end

@implementation MyURLProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    return ![[[self class] propertyForKey:kOurRecursiveRequestFlagProperty inRequest:request] boolValue];
}

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id <NSURLProtocolClient>)client {
    return [super initWithRequest:request cachedResponse:cachedResponse client:client];
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    return request;
}

- (void)startLoading {
    NSMutableURLRequest* mutableRequest = [[self request] mutableCopy];
    [[self class] setProperty:@YES forKey:kOurRecursiveRequestFlagProperty inRequest:mutableRequest];
    // Associate any non-property list object with mutableRequest to reproduce issue.
    [[self class] setProperty:[UIApplication sharedApplication] forKey:@"dummyKey" inRequest:mutableRequest];
    self.connection = [[NSURLConnection alloc] initWithRequest:mutableRequest delegate:self startImmediately:YES];
}

- (void)stopLoading {
}

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

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

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

- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse {
    return request;
}

@end

UPD2 目前,我认为缓存是罪魁祸首。即使我使用NSURLCacheStorageNotAllowed,此代码仍会尝试创建缓存字典,并将关联的[UIApplication sharedApplication]写入其中。

  

2016-03-20 20:52:19.630 WebViewDemo [7102:663895] ADD:无法在path = / Users / xxx / Library / Developer / CoreSimulator / Devices / 89D7C50F-939B-4360-A19F-创建缓存字典4547AE4F7515 /数据/容器/数据/应用/ 6F05411C-0261-4A33-9531-9E4E900C4910 /库/缓存/ Test.WebViewDemo。键= 0x7fe76af45f90

所以,接下来我的问题是 - 如何在不实现自己的关联字典的情况下禁用此缓存。我应该这样做,还是我应该避免setProperty:forKey:inRequest:

最重要的是,使用setProperty:forKey:inRequest:来存储kOurRecursiveRequestFlagProperty是正确的,正如Apple sample所暗示的那样?我还在试图弄明白何时可以使用这种方法,何时无法使用。

1 个答案:

答案 0 :(得分:2)

IIRC,数据必须是可序列化的原因是,在后台任务中,URL请求被序列化并通过XPC发送到实际执行URL提取的后台守护进程。如果数据不可序列化,则会在传输过程中丢失。

您在此处存储的对象需要是小型的,自包含的数据,以便您的协议识别该请求

通常,如果您需要将更复杂的内容与请求相关联,则可以使用此方法来存储UUID或其他任意字符串。然后,您将在应用程序中的正常NSDictionary中存储与键相同的字符串,在告诉客户端请求已完成/失败后处理字典键和关联数据。