美文网首页
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;

相关文章

  • Dispatch I/O 本地读取

    1.Dispatch I/O 简单介绍 在《iOS和OSX多线程和内存管理》上看到Dispatch I/O的介绍,...

  • iOS I/O与多线程

    沙盒模型 特点 安全 隐私 整洁 sandbox Bundle Containeryixin.app[[NSBun...

  • python-多线程编程

    串行程序必须使用非阻塞I/O,或拥有计时器的阻塞I/O,需兼顾要执行的多个任务,难以维护。 多线程编程,以及多线程...

  • Netty权威指南:I/O 多路复用技术

    在I/O编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理。I/O多路复...

  • 对比python的I/O密集型单线程和aysncio协程并发

    前面比较了单线程与多线程的I/O密集型,多线程访问速度远远优于单线程,今天我们再测试下单线程与协程并发访问多个网页...

  • 记一次腾讯面试

    iOS 腾讯大佬问的面试题: 1.iOS中.o文件了解吗?说下.o文件和内存的关系; 2.runloop和多线程的...

  • Lesson03

    异步式I/O与事件紧密结合 同步式I/O与异步式I/O的区别

  • 多线程同步I/O与单线程异步I/O(笔记)

    学习nodejs时,看到它的简介说它是采用异步I/O与事件驱动的架构设计,搜了很多关于同步I/O和异步I/O的资料...

  • 多线程基础

    为什么要学习多线程? 耗时操作的问题演示 模拟耗时操作 结论空的for循环不耗时I/O操作是非常非常耗时的I/O操...

  • 走进内核第一步(XNU内核简述)

    目录 简述 Mach任务与线程端口虚拟地址任务地址空间 BSD I/O kit 简述 XNU内核是iOS的核心,与...

网友评论

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

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