您的位置:首页 > 理论基础 > 计算机网络

iOS网络开发编程之NSURLConnection详解

2015-03-03 21:07 387 查看
iOS网络层常用的库如ASIHTTPRequest,AFNetworking,MKNetworkKit等知名的第三方库。随着ASI不再更新,楼主基本上也跟着大部队用了AF。AF用的是Cocoa层的API-NSURLConnection。

以前只是简简单单的用过NSURLConnection,很多相关的方法都不是很熟悉,今天抽空了系统的学习了下,晚上顺道总结下NSURLConnection的用法。

一、NSURLConnection的属性及方法。

进入NSURLConnection.h,自上而下介绍所有的方法。

@interface NSURLConnection :NSObject
{

    @private

    NSURLConnectionInternal *_internal;
}

/* Designated initializer */

/*

创建一个NSURLConnection,只建立连接,并没有下载数据

request: 请求内容

delegate:NSURLConnectionDelegate,NSURLConnection实例会强引用delegate,直到回调didFinishLoading,didFailWithErrorNSURLConnection.cancel调用.(During
the download the connection maintains a strong reference to the 

delegate
. It releases that strong reference when the connection finishes loading, fails, or is canceled)

startImmediately : 是否立即下载数据,YES立即下载,并把connection加入到当前的runloop中。NO,只建立连接,不下载数据,需要手动

【connection start】开始下载数据。

*/
- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate
startImmediately:(BOOL)startImmediately
NS_AVAILABLE(10_5,
2_0);

/*

其实就是用的[self initWithRequest:request delegate:delegate startImmediately:YES];

不需要显示的在去调用【connection start】

*/

- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate;

/*

其实就是用的[self initWithRequest:request delegate:delegate startImmediately:YES];

不需要显示的在去调用【connection start】

*/

+ (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;

/*

建立连接时用的请求

*/

@property (readonly,copy)
NSURLRequest *originalRequestNS_AVAILABLE(10_8,5_0);

/*

建立连接的请求进过认证协议可能会改变

As the connection performs the load,

        this request may change as a result of protocol

        canonicalization or due to following redirects.

        -currentRequest can be used to retrieve this value.

*/

@property (readonly,copy)
NSURLRequest *currentRequestNS_AVAILABLE(10_8,5_0);

/*

开始下载数据,通过- (instancetype)initWithRequest:(NSURLRequest
*)request delegate:(id)delegate startImmediately:(BOOL)startImmediately
初始化的实例,调用【connection start】

*/

- (void)startNS_AVAILABLE(10_5,2_0);
/*
断开网络连接,取消请求,cancel方法不能保证代理回调立即不会调用(应该是请求到的数据,只能传给代理),cancel会release delegate
*/

- (void)cancel;

/*

将connection实例回调加入到一个runloop,NSURLConnectionDelegate回调会在这个runloop中响应

注意该方法不能跟setDelegateQueue同时设置,只能选择一个。

*/

- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)modeNS_AVAILABLE(10_5,2_0);
/*
取消在这个runloop中的回调
*/

- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)modeNS_AVAILABLE(10_5,2_0);

/*
如果设置了queue,回调将会在这个queue上进行,回调一次就生成了一个NSBlockOperation加入到了queue中

注意该方法不能跟scheduleInRunLoop同时设置,只能选择一个。

*/
- (void)setDelegateQueue:(NSOperationQueue*) queueNS_AVAILABLE(10_7,5_0);

@interface NSURLConnection (NSURLConnectionSynchronousLoading)

/*

类方法创建一个同步请求。这个方法是建立在异步的基础上,然后阻塞当前线程实现的

response:响应头信息,传递一个二维指针

error:请求结果的状态

*/
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse
**)response error:(NSError **)error;

@end

@interface NSURLConnection (NSURLConnectionQueuedLoading)

/*

发起一个异步请求

queue:completionHandler会运行在这个queue中

completionHandler:请求回调block

 */
+ (void)sendAsynchronousRequest:(NSURLRequest*) request
                          queue:(NSOperationQueue*) queue
              completionHandler:(void (^)(NSURLResponse* response,NSData*
data, NSError* connectionError)) handlerNS_AVAILABLE(10_7,5_0);

           

@end

      

二、NSURLConnection用法

上边方法介绍的差不多了,写几个小demo试试。

      首先定义一些基本配置 

static char *const URLSTRING ="http://f.hiphotos.baidu.com/image/h%3D200/sign=a1217b1330fa828bce239ae3cd1f41cd/0e2442a7d933c895cc5c676dd21373f082020081.jpg";

-(NSURLRequest*)request{
   NSString* urlString = [NSStringstringWithUTF8String:URLSTRING];
   NSURL* url = [NSURLURLWithString:urlString];

    NSURLRequest* request = [NSURLRequestrequestWithURL:url

                                             cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
                                        timeoutInterval:30.f];
   return request;
}

另外,抽象出来了一个NSURLConnectionDelegate类来实现NSURLConnectionDelegate,这样其他地方在用到NSURLConnection的时候就不需要在写一大堆协议了。

#import <Foundation/Foundation.h>

typedefvoid(^NSURLConnectionCompeletionBlock)(id);

@interface NSURLConnectionDelegate :NSObject<NSURLConnectionDataDelegate>

@property(nonatomic ,strong ,
readonly)NSOutputStream* os;

@property(nonatomic ,assign ,
readonly)BOOL isFinish;

@property(nonatomic ,strong ,
readonly)NSMutableData* buffer;

@property(nonatomic ,assign ,
readonly)NSUInteger contentLength;

@property(nonatomic ,strong)
NSURLConnectionCompeletionBlock completionBlock;

@end

#import "NSURLConnectionDelegate.h"

@implementation NSURLConnectionDelegate

- (void)dealloc
{

    NSLog(@"__%s__",__FUNCTION__);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

    if (self.completionBlock) {

        self.completionBlock([self.ospropertyForKey:NSStreamDataWrittenToMemoryStreamKey]);
    }
    [self.osclose];

    _isFinish =
YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse
*)responsez{
   if ([responsez
isKindOfClass:[NSHTTPURLResponseclass]]) {

        NSHTTPURLResponse* hr = (NSHTTPURLResponse*)responsez;
       if (hr.statusCode ==200) {

            _contentLength = hr.expectedContentLength;

//            _os = [NSOutputStream outputStreamToFileAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/image.jpg"] append:NO];

            _os = [NSOutputStreamoutputStreamToMemory];

            [_osopen];
        }
    }
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError
*)error{

    if (self.completionBlock) {
       NSError* error = [NSErrorerrorWithDomain:error.domain
                                            code:error.code
                                        userInfo:error.userInfo];
       self.completionBlock(error);
    }

    _isFinish =
YES;
    [self.osclose];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData
*)data{
   if (!self.buffer) {

        _buffer = [NSMutableDatadataWithCapacity:self.contentLength];
    }
    [self.bufferappendData:data];

    
   //

    
   NSUInteger totalLength = data.length;
   NSUInteger totalWirte =
0;
   const
uint8_t * datas = data.bytes;
   while (totalWirte < totalLength) {

        
       /*

         The number of bytes actually written, or -1 if an error occurs. More information about the error can be obtained with streamError. If the receiver is a fixed-length stream and has reached its capacity, 0 is returned.

         */
       NSInteger writeLength = [self.oswrite:&datas[totalWirte]
maxLength:(totalLength - totalWirte)];
       if (writeLength == -1) {
            [connectioncancel];
           break;
        }
        totalWirte += writeLength;

        NSLog(@"totalLenght = %lu , totalWirte = %lu",totalLength,totalWirte);
    }

    

    

    
}

配合写个NSOperation

#import <Foundation/Foundation.h>

@interface NSURLConnectionOperation :NSOperation

@property(nonatomic ,strong)
NSURLRequest* request;

@property(nonatomic ,strong )
NSData* buffer;

-(instancetype)initWithRequest:(NSURLRequest*)request;
-(void)startAsync;

@end

#import "NSURLConnectionOperation.h"

#import "NSURLConnectionDelegate.h"

@interface
NSURLConnectionOperation ()

@property (nonatomic,assign,
getter=isExecuting)BOOL executing;

@property (nonatomic,assign,
getter=isFinished)BOOL finished;

@property (nonatomic,assign,
getter=isConcurrent)BOOL concurrent;

@property(nonatomic ,strong )
NSThread* thread;

@property(nonatomic ,strong  )
NSURLConnection* connection;

@end

@implementation NSURLConnectionOperation
@synthesize executing=_executing,finished=_finished,concurrent=_concurrent;

- (void)dealloc
{

    NSLog(@"%s",__FUNCTION__);
}

-(instancetype)initWithRequest:(NSURLRequest *)request{
   self = [superinit];
   if (self) {
       self.request = request;
    }

    return
self;
}
- (void)start{
   if (!self.thread) {
       _thread = [NSThreadcurrentThread];
    }
   self.finished =NO;
   self.executing =YES;

    __weakNSURLConnectionOperation* wkSelf =
self;

    NSURLConnectionDelegate* delegate = [NSURLConnectionDelegatenew];
    delegate.completionBlock = ^(id data){
       if (!wkSelf.buffer) {
            wkSelf.buffer = data;
        }

        //广播通知,执行completionBlock
        wkSelf.finished =YES;
        wkSelf.executing =NO;

        
    };

    _connection = [[NSURLConnectionalloc]
initWithRequest:self.request
                                                 delegate:delegate
//保持delegate强引用
                                         startImmediately:NO];

    //start前手动设置runloop为默认

    [_connectionscheduleInRunLoop:[NSRunLoopcurrentRunLoop]
forMode:NSDefaultRunLoopMode];

    

    [_connection
start];

    
   while (!self.isFinished) {

        [[NSRunLoopcurrentRunLoop]
runMode:NSDefaultRunLoopModebeforeDate:[NSDatedistantFuture]];
    }

    NSLog(@"start end!!");

    
}
- (void)startAsync{

    _thread = [[NSThreadalloc]
initWithTarget:selfselector:@selector(start)object:nil];
    [self.threadstart];
}

- (void)setFinished:(BOOL)finished{

    [selfwillChangeValueForKey:@"isFinished"];
   _finished = finished;

    [selfdidChangeValueForKey:@"isFinished"];

}
- (void)setExecuting:(BOOL)executing{

    [selfwillChangeValueForKey:@"isExecuting"];
   _executing = executing;

    [selfdidChangeValueForKey:@"isExecuting"];
}
- (BOOL)isConcurrent{

    return
YES;
}

@end

同步请求:

-(IBAction)sync:(id)sender{
    [selfprintGO];

    
   //
   NSURLResponse* response =
nil;
   NSError* error =
nil;

    NSData* data = [NSURLConnectionsendSynchronousRequest:[selfrequest]
                                        returningResponse:&response
                                                    error:&error];

    

    NSLog(@"get sync data!");
   if ([response
isKindOfClass:[NSHTTPURLResponseclass]]) {

        NSHTTPURLResponse* hr = (NSHTTPURLResponse*)response;
       if (hr.statusCode ==200) {

            NSLog(@"sync repsonse head: \n%@",hr.allHeaderFields);
           self.imageView.image = [UIImageimageWithData:data];
        }
    }

    
    [selfprintEnd];

    
}

-(IBAction)sync:(id)sender{
    [selfprintGO];

    NSURLConnectionOperation* operation = [[NSURLConnectionOperationalloc]
initWithRequest:[selfrequest]];
   __weak
NSURLConnectionOperation* wkOp = operation;
    operation.completionBlock = ^{

        __strongNSURLConnectionOperation* sOp = wkOp;//防止wkOp被释放,强引用
       NSLog(@"set ?");

        dispatch_async(dispatch_get_main_queue(), ^{
           self.imageView.image = [UIImageimageWithData:sOp.buffer];
        });
    };
    [operationstart];
    [selfprintEnd];
}

异步请求:

-(IBAction)async:(id)sender{

    
    [selfprintGO];

    //*************************** NSURLConnection async

    //该方法只能简单发起请求,等待结果,统计进度不方便。

    [NSURLConnectionsendAsynchronousRequest:[selfrequest]
                                      queue:self.queue//completionHandler
run on  this queue
                          completionHandler:^(NSURLResponse *response,NSData
*data, NSError *connectionError) {
                              if ([response
isKindOfClass:[NSHTTPURLResponseclass]]) {
                                  NSHTTPURLResponse* hr = (NSHTTPURLResponse*)response;
                                  if (hr.statusCode ==200) {
                                       [selfprintCurrentThread];
                                      self.imageView.image = [UIImageimageWithData:data];
                                   }
                               }
                           }];

    

    
    [selfprintEnd];
    

}

-(IBAction)async:(id)sender{

    
    [selfprintGO];

    NSURLConnectionDelegate* connectionDelegate = [NSURLConnectionDelegatenew];
    connectionDelegate.completionBlock = ^(id data){

        [selfprintCurrentThread];
       if ([data
isKindOfClass:[NSDataclass]]) {

            [[NSOperationQueuemainQueue]
addOperationWithBlock:^{
               self.imageView.image = [UIImageimageWithData:data];

            }];
        }
    };

    

    
   NSURLConnection* connection = [[NSURLConnectionalloc]
initWithRequest:[selfrequest]
delegate:connectionDelegate];

    //delegate回调在当前operationqueue开辟的线程中完成

//    [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [connectionsetDelegateQueue:self.queue];
    [connectionstart];

    
    [selfprintEnd];
   

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息