iOS 大神之路iOS开发技术程序员

NSOperation与网络封装(上)

2016-11-22  本文已影响1029人  Noah1985

关于NSOperation

基于上面这些特点,它很适合用来做网络请求封装。尤其是需要将任务组合起来的,如上传队列和下载队列。

分析需求,定义接口

Screen Shot 2016-11-22 at 10.42.39 AM.png

在开始实现想法前画一下图是一个很好的习惯,有助于整理自己的思维并逐步推进。

实现一个大目标前,先从小目标入手,逐个完成。先考虑一下我们这个Operation需要什么功能。

  1. 开始任务:继承自NSOperation后就有一个start方法。

@interface CustomOperation : NSOperation

@property (nonatomic, readonly) id result;
@property (nonatomic, readonly) NSError *error;
@property (nonatomic, readonly) double progress;

+ (instancetype)getWithUrlString:(NSString *)urlString
                       parameters:(NSDictionary<NSString *, NSString *> *)parameters;

+ (instancetype)postWithUrlString:(NSString *)urlString
                      parameters:(NSDictionary<NSString *, NSString *> *)parameters;

+ (instancetype)downloadWithUrlString:(NSString *)urlString;

+ (instancetype)uploadWithUrlString:(NSString *)urlString
                         parameters:(NSDictionary<NSString *, NSString *> *)parameters
                               data:(NSData *)data;
@end

先实现最基本的需求,这里我们需要一个网络封装类。Operation只是一个网络请求封装,从UML图可以看出来,实际工作的是另一个网络封装。面向对象的设计原则,保持对象的功能单一。

我们这里还却一个网络封装类,但这里只谈接口不谈实现。所谓的面向接口编程,这点很重要。第一版的需求先将网络封装私有化。

@interface Network : NSObject
+ (instancetype)share;
- (NSURLSessionDataTask *)dataTaskWithUrlString:(NSString *)urlString
                                     method:(NSString *)method
                                 parameters:(NSDictionary<NSString *, NSString *> *)parameters
                                   callBack:(void(^)(NSError *,id result))callBack;


- (NSURLSessionDownloadTask *)downloadTaskWithUrlString:(NSString *)urlString
                                               progress:(void(^)(double))progress
                                   callBack:(void(^)(NSError *,id result))callBack;

- (NSURLSessionDataTask *)uploadTaskWithUrlString:(NSString *)urlString
                                       parameters:(NSDictionary<NSString *, NSString *> *)parameters
                                       uploadData:(NSData *)data
                                           progress:(void(^)(double))progress
                                           callBack:(void(^)(NSError *, id result))callBack;
@end

这不是一个很严禁的接口,但对于本文来说足够了。实际项目里需要根据需求来修改。

Operation的实现

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

@implementation
// 因为父类的属性是Readonly的,重载时如果需要setter的话则需要手动合成。
@synthesize finished = _finished, executing = _executing;

// 这里需要实现KVO相关的方法,NSOperationQueue是通过KVO来判断任务状态的
- (void)setFinished:(BOOL)finished {
    [self willChangeValueForKey:@"isFinished"];
    _finished = finished;
    [self didChangeValueForKey:@"isFinished"];
}

- (void)setExecuting:(BOOL)executing {
    [self willChangeValueForKey:@"isExecuting"];
    _executing = executing;
    [self didChangeValueForKey:@"isExecuting"];
}
- (void)start {
    
    if (self.isCancelled) {
        self.finished = YES;
        return;
    }
    self.executing = YES;
}

- (void)cancel {
    [super cancel];
    
    // 如果正在执行中则表示已经start过,可以将isFinished设为yes
    if (self.isExecuting) {
        self.finished = YES;
        self.executing = NO;
    }
}
@property (nonatomic, readwrite) id result;
@property (nonatomic, readwrite) NSError *error;
@property (nonatomic, readwrite) double progress;

@property (nonatomic, copy) NSString *urlString;
@property (nonatomic, copy) NSDictionary<NSString *, NSString *> *parameters;
@property (nonatomic, copy) NSData *uploadData;
@property (nonatomic, assign) OperationType type;
@property (nonatomic, strong) NSURLSessionTask *task;


+ (instancetype)getWithUrlString:(NSString *)urlString
                      parameters:(NSDictionary<NSString *, NSString *> *)parameters {
    CustomOperation *op = [CustomOperation new];
    op.type = OperationTypeGet;
    op.urlString = urlString;
    op.parameters = parameters;
    return op;
}

+ (instancetype)postWithUrlString:(NSString *)urlString
                       parameters:(NSDictionary<NSString *, NSString *> *)parameters {
    CustomOperation *op = [CustomOperation new];
    op.type = OperationTypePost;
    op.urlString = urlString;
    op.parameters = parameters;
    return op;
}

+ (instancetype)downloadWithUrlString:(NSString *)urlString {
    CustomOperation *op = [CustomOperation new];
    op.type = OperationTypeDownload;
    op.urlString = urlString;
    return op;

}

+ (instancetype)uploadWithUrlString:(NSString *)urlString
                         parameters:(NSDictionary<NSString *, NSString *> *)parameters
                               data:(NSData *)data {
    CustomOperation *op = [CustomOperation new];
    op.type = OperationTypeUpload;
    op.urlString = urlString;
    op.parameters = parameters;
    op.uploadData = data;
    return op;

}

- (void)start {
    
    if (self.isCancelled) {
        self.finished = YES;
        return;
    }
    
    [self handleNetwork];
    self.executing = YES;
}

- (void)cancel {
    [super cancel];
    
    // 如果正在执行中则表示已经start过,可以将isFinished设为yes
    if (self.isExecuting) {
        self.finished = YES;
        self.executing = NO;
    }
    
    [self.task cancel];
    self.task = nil;
}

- (void)handleNetwork {
    Network *network = [Network share];
    if (self.type == OperationTypeGet) {
        self.task = [network dataTaskWithUrlString:self.urlString
                                method:@"GET"
                            parameters:self.parameters callBack:^(NSError *error, id result) {
            self.error = error;
            self.result = result;
            self.finished = YES;
            self.executing = NO;
        }];
    }
    if (self.type == OperationTypePost) {
        self.task = [network dataTaskWithUrlString:self.urlString
                                method:@"POST"
                            parameters:self.parameters callBack:^(NSError *error, id result) {
            self.error = error;
            self.result = result;
            self.finished = YES;
            self.executing = NO;
        }];
    }
    if (self.type == OperationTypeDownload) {
        self.task = [network downloadTaskWithUrlString:self.urlString
                                              progress:^(double progress) {
            self.progress = progress;
        } callBack:^(NSError *error, id result) {
            self.error = error;
            self.result = result;
            self.finished = YES;
            self.executing = NO;

        }];
    }
    if (self.type == OperationTypeUpload) {
        self.task = [network uploadTaskWithUrlString:self.urlString
                                          parameters:self.parameters
                                          uploadData:self.uploadData
                                            progress:^(double progress) {
            self.progress = progress;
        } callBack:^(NSError *error, id result) {
            self.error = error;
            self.result = result;
            self.finished = YES;
            self.executing = NO;

        }];
    }
    [self.task resume];

}

总结

一个简单的请求封装就这样实现了,但还是缺乏点什么,有经验的人会发现这里缺少了回调,但即使没有回调功能,这个封装还是完整的,我们可以将回调作为一个扩展功能来实现,通过Category来分拆功能模块。回调和NSOperationQueue的实现就留到下一篇文章,敬请期待!

上一篇 下一篇

猜你喜欢

热点阅读