iOS I/O与多线程

2017-02-10  本文已影响0人  owenqi

沙盒模型

特点

sandbox

资源管理器

NSFileManager *manager = [[NSFileManager alloc] init];
NSFileManager *defaultManager = [NSFileManager defaultManager];

多线程

NSThread

// 初始化
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
// 启动
- (void)start;

// 类方法, 会自动启动
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

// 使用继承, 需要使用 start 方法启动, 使用 delegate 将线程执行的结果传出
@interface MyThread : NSThread
@end
@implementation
- (void)main {
    // 在这里重写线程实现的方法
    NSLog(@"thread start");
}
@end
- (void)viewDidLoad {
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage:) object:nil];
    [thread start];
}
- (void)downloadImage:(id)arg {
    NSURL *url = [NSURL urlWithString:@"http://xxx.com/image.jpg"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    
    [self performSelectorOnMainThread:@selector(onImageDone:) withObject:image waitUntilDone:NO];
}
- (void)onImageDone:(id)arg {
    if ([arg isKindOfClass:[UIImage class]]) {
        self.imageView.image = (UIImage *)arg;
    }
}
+ (BOOL)isMainThread;
+ (NSThread *)mainThread;
+ (NSThread *)currentThread;

// 暂停线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
unsigned int sleep(unsigned int);

// 线程通信
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorOnBackground:(SEL)aSelector withObject:(nullable id)argA;

多线程优缺点

多线程同时访问资源

@property (nonatomic, strong) NSLock *lock;
_lock = [[NSLock alloc] init];

[_lock lock]; // 加锁
// 这里执行的代码会把使用的成员变量锁住
[_lock unlock]; // 解锁

// 过程猜测: 使用 [_lock lock] 的时候, 如果 _lock 在锁住状态, 则这个操作会一直等待, 
// 等到操作执行完之后, _lock 被解锁, 才能执行下次加锁操作
@synchronized (self) {
    // 执行操作, 这里的操作会被加锁, 执行完后退出 synchronized 则会被解锁
}

死锁状态

GCD (Grand Central Dispatch)

// 队列
dispatch_quequ_t

// 创建
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

// 使用
void
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
void
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

单例

+ (instancetype)sharedObject {
    static SingletonObject *instance = nil;
    
    // 下面的两行代码只会被之行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[SingletonObject alloc] init];
    });
    return instance;
}

dispatch_semaphore (信号量)

dispatch_semaphore_t

// 创建
dispatch_semaphore_t
dispatch_semaphore_create(long value);

// 触发信号量
long
dispatch_semaphore_signal(dispatch_semaphore_t dsema);

// 等待信号量
long 
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

NSOperation

  1. 创建 NSOperationQueue : NSOperationQueue *queue = [[NSOperationQueue alloc] init];

  2. 创建 NSOperation 子类对象 : 重写 NSOperation 任务执行函数

    @interface  MyOperation : NSOperation
    @end
    @implementation MyOperation
    - (void)main {
        // 任务执行函数
    }
    
  3. 将 NSOperation 的子类对象加入 NSOperationQueue : [queue addOperation:operation];

  4. 设置子类对象的 completionBlock, 在 block 中进行剩余的 UI 操作

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    MyOperation *op = [[MyOperation alloc] init];
    __weak typeof(op) weakOp = op;
    op.completionBlock = ^(){
        // 执行完毕之后需要进行的操作
        // 在此的操作也是在子线程进行的, 所以如果涉及到 UI 的操作需要手动放到主线程中
    }
    

NSBlockOperation

+ (instancetype)blockOperationWithBlock:(void (^)(void))block;

NSInvocationOperation

+ (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;

Serial vs Concurrent

@property NSInteger maxConcurrentOperationCount;
// maxConcurrentOperationCount = 1      Serial Queue
// maxConcurrentOperationCount > 1      Concurrent Queue
// [NSOperationQueue mainQueue]         dispatch_get_main_queue()
@property (readonly) NSUinteger operationCount;

GCD vs NSOperation

NSOperation 等待

使用 dependency 等待任务

@interface NSOperation : NSObject
- (void)addDependency:(NSOperation *)op; // 添加依赖任务
- (void)removeDependency:(NSOperation *)op; // 移除依赖任务
@property (readonly, copy) NSArray<NSOperation *> *dependencies;

优先级

@interface NSOperation : NSObject
@property NSOperationQueuePriority queuePriority;

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L;
    NSOperationQueuePriorityLow     = -4L;
    NSOperationQueuePriorityNormal  = 0;
    NSOperationQueuePriorityHigh        = 4;
    NSOperationQueuePriorityVeryHigh    = 8;
}

RunLoop 常驻的主线程

@interface NSRunLoop : NSObject
+ (NSRunLoop *)currentRunLoop;
+ (NSRunLoop *)mainRunLoop;

- (void)run;
- (void)runUntilDate:(NSDate *)limitDate;
@end

常驻的后台线程

RunLoop Mode

@interface NSRunLoop : NSObject
- (void)addTimer:(NSTimer *)timer forMode:(NSString *);
@end

iOS 网络基础

NSURLRequest

/********** 创建请求 **********/
@interface NSURLRequest : NSObject
// 各种属性都不可设置
+ (instancetype)requestWithURL:(NSURL *)URL;
- (instancetype)initWithURL:(NSURL *)URL;
@property (nullable, readonly, copy) NSURL *URL;

@property (nullable, readonly, copy) NSString *HTTPMethod;
@property (nullable, readonly, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;
@property (nullable, readonly, copy) NSData *HTTPBody;
@end

@interface : NSMutableURLRequest : NSURLRequest
// ... 各种属性都是可以设置的

@interface NSURL : NSObject
- (nullable instancetype)initWithString:(NSString *)URLString;
+ (nullable instancetype)URLWithString:(NSString *)URLString;
@property (readonly, copy) NSString *absoluteString;
@end


/********** 发送请求 **********/
@interface NSURLConnection : NSObject // 逐步被废弃, 推荐使用 NSURLSession
- (nullable instancetype)initWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate startImmediately:(BOOL)startImmediately;
- (void)start;
@end


/********** 接收响应 **********/
@protocol NSURLConnectionDataDelegate <NSURLConnectionDelegate>
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
@end

@protocol NSURLConnectionDelegate <NSObject>
@optional
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
@end

@interface NSHTTPURLResponse : NSURLResponse
@property (readonly) NSInteger statusCode;
@property (readonly, copy) NSDictionary *allHeaderFields;
@end

@interface NSURLResponse : NSObject <NSSecureCoding, NSCopying>
@property (nullable, readonly, copy) NSURL *URL;
@end


/********** 获取数据 : 解析 JSON/XML 数据 **********/
// JSON  <==> NSData
@interface NSJSONSeriallization : NSObject
+ (BOOL)isValidJSONObject:(id)obj;
+ (nullable NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;
+ (nullable id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;

流程 Demo

// 创建一个请求
NSURL *url = [NSURL URLWithString:@"http://xxx.com"];
NSMutableURLRequest *request = [NSMutableRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:@"NEDemoAgent" forHTTPHeaderField:@"User-Agent"];
[request setValue:@"Application/JSON" forHTTPHeaderField:@"Content-Type"];

// 发送请求
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection start];

// 接收响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    self.response = response;
    self.responseData = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    // handle error.
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // handle when request finished.
    NSStringEncoding stringEncoding = NSUTF8StringEncoding;
    // 直接获取返回的字符串
    self.responseString = [[NSString alloc] initWithData:self.responseData 
                                encoding:stringEncoding];
    // self.responseInfo 是 id 类型, 在这里获取的是格式化之后的 JSON 数据, 可能是 NSArray 或者 NSDictionary
    self.responseInfo = [NSJSONSerialization JSONObjectWithData:self.responseData 
                                options:0 
                                error:nil];
}

使用 NSURLSession 代替 NSURLConnection

NSURLSession NSURLConnection
NSURLConnection NSURLSession & NSURLSessionTask & NSURLSessionConfiguration
NSURLConnectionDelegate && NSURLConnectionDatDelegate NSURLSessionDelegate
@interface NSURLSession : NSObject
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
上一篇 下一篇

猜你喜欢

热点阅读