mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-03-30 17:43:22 +08:00
241 lines
8.3 KiB
Objective-C
241 lines
8.3 KiB
Objective-C
//
|
|
// RKObjectRequestOperation.m
|
|
// RestKit
|
|
//
|
|
// Created by Blake Watters on 8/9/12.
|
|
// Copyright (c) 2012 RestKit. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#import "RKObjectRequestOperation.h"
|
|
#import "RKResponseMapperOperation.h"
|
|
#import "RKMIMETypeSerialization.h"
|
|
#import "RKHTTPUtilities.h"
|
|
#import "RKLog.h"
|
|
|
|
// Set Logging Component
|
|
#undef RKLogComponent
|
|
#define RKLogComponent lcl_cRestKitNetwork
|
|
|
|
static inline NSString *RKDescriptionForRequest(NSURLRequest *request)
|
|
{
|
|
return [NSString stringWithFormat:@"%@ '%@'", request.HTTPMethod, [request.URL absoluteString]];
|
|
}
|
|
|
|
static NSIndexSet *RKObjectRequestOperationAcceptableMIMETypes()
|
|
{
|
|
static NSMutableIndexSet *statusCodes = nil;
|
|
if (! statusCodes) {
|
|
statusCodes = [NSMutableIndexSet indexSet];
|
|
[statusCodes addIndexesInRange:RKStatusCodeRangeForClass(RKStatusCodeClassSuccessful)];
|
|
[statusCodes addIndexesInRange:RKStatusCodeRangeForClass(RKStatusCodeClassClientError)];
|
|
}
|
|
return statusCodes;
|
|
}
|
|
|
|
@interface RKObjectRequestOperation ()
|
|
@property (nonatomic, strong, readwrite) RKHTTPRequestOperation *requestOperation;
|
|
@property (nonatomic, strong, readwrite) NSArray *responseDescriptors;
|
|
@property (nonatomic, strong, readwrite) RKMappingResult *mappingResult;
|
|
@property (nonatomic, strong, readwrite) NSError *error;
|
|
@property (nonatomic, strong, readwrite) NSURLRequest *request;
|
|
@property (nonatomic, strong) NSCachedURLResponse *cachedResponse;
|
|
@end
|
|
|
|
@implementation RKObjectRequestOperation
|
|
|
|
- (void)dealloc
|
|
{
|
|
if(_failureCallbackQueue) dispatch_release(_failureCallbackQueue);
|
|
if(_successCallbackQueue) dispatch_release(_successCallbackQueue);
|
|
}
|
|
|
|
- (id)initWithRequest:(NSURLRequest *)request responseDescriptors:(NSArray *)responseDescriptors
|
|
{
|
|
NSParameterAssert(request);
|
|
NSParameterAssert(responseDescriptors);
|
|
|
|
self = [self init];
|
|
if (self) {
|
|
self.request = request;
|
|
self.responseDescriptors = responseDescriptors;
|
|
self.requestOperation = [[RKHTTPRequestOperation alloc] initWithRequest:request];
|
|
self.requestOperation.acceptableContentTypes = [RKMIMETypeSerialization registeredMIMETypes];
|
|
self.requestOperation.acceptableStatusCodes = RKObjectRequestOperationAcceptableMIMETypes();
|
|
|
|
// Initialize avoidsNetworkAccess based on caching preferences
|
|
switch(self.request.cachePolicy) {
|
|
case NSURLRequestReloadIgnoringLocalCacheData:
|
|
case NSURLRequestReloadIgnoringLocalAndRemoteCacheData:
|
|
self.avoidsNetworkAccess = NO;
|
|
break;
|
|
|
|
default:
|
|
self.avoidsNetworkAccess = YES;
|
|
break;
|
|
};
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)setSuccessCallbackQueue:(dispatch_queue_t)successCallbackQueue
|
|
{
|
|
if (successCallbackQueue != _successCallbackQueue) {
|
|
if (_successCallbackQueue) {
|
|
dispatch_release(_successCallbackQueue);
|
|
_successCallbackQueue = NULL;
|
|
}
|
|
|
|
if (successCallbackQueue) {
|
|
dispatch_retain(successCallbackQueue);
|
|
_successCallbackQueue = successCallbackQueue;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setFailureCallbackQueue:(dispatch_queue_t)failureCallbackQueue
|
|
{
|
|
if (failureCallbackQueue != _failureCallbackQueue) {
|
|
if (_failureCallbackQueue) {
|
|
dispatch_release(_failureCallbackQueue);
|
|
_failureCallbackQueue = NULL;
|
|
}
|
|
|
|
if (failureCallbackQueue) {
|
|
dispatch_retain(failureCallbackQueue);
|
|
_failureCallbackQueue = failureCallbackQueue;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setCompletionBlockWithSuccess:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success
|
|
failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure
|
|
{
|
|
__block RKObjectRequestOperation *_blockSelf = self;
|
|
self.completionBlock = ^ {
|
|
if ([_blockSelf isCancelled]) {
|
|
return;
|
|
}
|
|
|
|
if (_blockSelf.error) {
|
|
if (failure) {
|
|
dispatch_async(_blockSelf.failureCallbackQueue ? _blockSelf.failureCallbackQueue : dispatch_get_main_queue(), ^{
|
|
failure(_blockSelf, _blockSelf.error);
|
|
});
|
|
}
|
|
} else {
|
|
if (success) {
|
|
dispatch_async(self.successCallbackQueue ? _blockSelf.successCallbackQueue : dispatch_get_main_queue(), ^{
|
|
success(_blockSelf, _blockSelf.mappingResult);
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
- (BOOL)isResponseFromCache
|
|
{
|
|
return self.cachedResponse != nil;
|
|
}
|
|
|
|
- (NSHTTPURLResponse *)response
|
|
{
|
|
return (NSHTTPURLResponse *) (self.isResponseFromCache ? self.cachedResponse.response : self.requestOperation.response);
|
|
}
|
|
|
|
- (NSData *)responseData
|
|
{
|
|
return self.isResponseFromCache ? self.cachedResponse.data : self.requestOperation.responseData;
|
|
}
|
|
|
|
- (RKMappingResult *)performMappingOnResponse:(NSError **)error
|
|
{
|
|
// Spin up an RKObjectResponseMapperOperation
|
|
RKObjectResponseMapperOperation *mapperOperation = [[RKObjectResponseMapperOperation alloc] initWithResponse:self.response
|
|
data:self.responseData
|
|
responseDescriptors:self.responseDescriptors];
|
|
mapperOperation.targetObject = self.targetObject;
|
|
[mapperOperation start];
|
|
[mapperOperation waitUntilFinished];
|
|
if (mapperOperation.error) {
|
|
if (error) *error = mapperOperation.error;
|
|
return nil;
|
|
}
|
|
return mapperOperation.mappingResult;
|
|
}
|
|
|
|
- (void)willFinish
|
|
{
|
|
// Default implementation does nothing
|
|
}
|
|
|
|
- (NSCachedURLResponse *)validCachedResponseForRequest:(NSURLRequest *)request
|
|
{
|
|
if (! self.avoidsNetworkAccess) {
|
|
RKLogDebug(@"avoidsNetworkAccess=NO: Skipping network access optimization.");
|
|
return nil;
|
|
}
|
|
|
|
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
|
|
if (cachedResponse) {
|
|
// Verify that the entry is valid
|
|
NSHTTPURLResponse *response = (NSHTTPURLResponse *) [cachedResponse response];
|
|
NSDate *cacheExpirationDate = RKHTTPCacheExpirationDateFromHeadersWithStatusCode([response allHeaderFields], response.statusCode);
|
|
RKLogTrace(@"Found cached response for request %@ with expiration date: %@ (cachedResponse.headers=%@)", RKDescriptionForRequest(self.request), cacheExpirationDate, [response allHeaderFields]);
|
|
if ([(NSDate *)[NSDate date] compare:cacheExpirationDate] == NSOrderedAscending) {
|
|
return cachedResponse;
|
|
}
|
|
} else {
|
|
RKLogDebug(@"No cached response available for request: %@", RKDescriptionForRequest(request));
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)main
|
|
{
|
|
if (self.isCancelled) return;
|
|
|
|
// See if we can satisfy the request without hitting the network
|
|
self.cachedResponse = [self validCachedResponseForRequest:self.request];
|
|
|
|
// Send the request
|
|
if (!self.cachedResponse) {
|
|
[self.requestOperation start];
|
|
[self.requestOperation waitUntilFinished];
|
|
} else {
|
|
RKLogDebug(@"Skipping networking access: Found valid cached response for request: %@", self.request);
|
|
}
|
|
|
|
if (self.requestOperation.error) {
|
|
RKLogError(@"Object request failed: Underlying HTTP request operation failed with error: %@", self.requestOperation.error);
|
|
self.error = self.requestOperation.error;
|
|
return;
|
|
}
|
|
|
|
// Map the response
|
|
NSError *error;
|
|
RKMappingResult *mappingResult = [self performMappingOnResponse:&error];
|
|
if (self.isCancelled) return;
|
|
if (! mappingResult) {
|
|
self.error = error;
|
|
return;
|
|
}
|
|
self.mappingResult = mappingResult;
|
|
[self willFinish];
|
|
}
|
|
|
|
@end
|