美文网首页
iOS I/O与多线程

iOS I/O与多线程

作者: owenqi | 来源:发表于2017-02-10 11:53 被阅读0次

    沙盒模型

    特点

    • 安全
    • 隐私
    • 整洁

    sandbox

    • Bundle Container
      • yixin.app
      • [[NSBundle mainBundle] pathForResource:@"name" ofType:@"type"]
    • Data Container
      • NSHomeDirectory()
      • Documents
      • [NSSearchPathForDirectoriesInDomains(NSDocumentDictionary, NSUserDomainMask, YES) firstObject]
      • Library
      • Temp
      • NSTemporaryDirectory()

    资源管理器

    • 发现资源 : 遍历目录
    • 修改资源 : 创建目录, 创建文件, 修改目录, 修改文件, 读写目录, 读写文件
    NSFileManager *manager = [[NSFileManager alloc] init];
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    

    多线程

    • 线程生而不平等 : 主线程 & 后台线程; 线程有优先级的关系
    • 线程是一个被模拟出来的概念 : CPU 通过分配时间片模拟出多线程同时工作的状态

    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;
        }
    }
    
    • 使用 cancel 将线程状态标记成取消状态
    + (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;
    

    多线程优缺点

    • 优点
      • 提高 APP 实时响应性
      • 充分利用计算资源
    • 缺点
      • 额外的系统开销
      • 线程同步问题
      • 程序复杂度上升

    多线程同时访问资源

    • 使用 NSLock
    @property (nonatomic, strong) NSLock *lock;
    _lock = [[NSLock alloc] init];
    
    [_lock lock]; // 加锁
    // 这里执行的代码会把使用的成员变量锁住
    [_lock unlock]; // 解锁
    
    // 过程猜测: 使用 [_lock lock] 的时候, 如果 _lock 在锁住状态, 则这个操作会一直等待, 
    // 等到操作执行完之后, _lock 被解锁, 才能执行下次加锁操作
    
    • 使用 @synchronized
      • 简单
      • 不需要自己 unlock, 不容易产生死锁
    @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 支持取消
      • 标记成取消状态, 当执行到这个任务的时候, 任务不会被执行但是 completetionBlock 仍然会被执行
    • 封装, 能适应更复杂的操作和提供更精细化的操控

    NSOperation 等待

    • 创建两个 OperationQueue : uploadQueue, finalQueue
    • 在 uploadQueue 中添加所有的操作
    • 在 finalQueue 中添加后续的操作 finalOperation, 中间加入代码
      • [uploadQueue waitUntilAllOperationsAreFinished];
      • finalOperation 会等待到所有的 uploadQueue 中任务执行完然后执行

    使用 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 常驻的主线程

    • 和线程一一对应, 每个线程有且只有一个 RunLoop
    • 线程创建的时候并未有 RunLoop, 需要手动创建, 主线程除外
    • RunLoop 的创建发生在第一次获取它的时候(单例)
    • 只能在线程内部获取对应的 RunLoop (主线程 RunLoop 除外)
    @interface NSRunLoop : NSObject
    + (NSRunLoop *)currentRunLoop;
    + (NSRunLoop *)mainRunLoop;
    
    - (void)run;
    - (void)runUntilDate:(NSDate *)limitDate;
    @end
    

    常驻的后台线程

    • 将有繁重的操作的回调指定到固定的线程中执行
    • perform selector 指定到固定的线程执行

    RunLoop Mode

    • NSDefaultRunLoopMode
    • NSRunLoopCommonModes
      • default mode
      • modal mode
      • tracking mode UITrackingRunLoopMode
    @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;
    

    相关文章

      网友评论

          本文标题:iOS I/O与多线程

          本文链接:https://www.haomeiwen.com/subject/vqegittx.html