iOS网络开发编程之NSURLConnection详解

时间:2022-12-15 08:16:52

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*) queue NS_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];

   


}