iOS I/O與多線程

沙盒模型

特點(diǎn)

  • 安全
  • 隱私
  • 整潔

sandbox

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

資源管理器

  • 發(fā)現(xiàn)資源 : 遍歷目錄
  • 修改資源 : 創(chuàng)建目錄, 創(chuàng)建文件, 修改目錄, 修改文件, 讀寫目錄, 讀寫文件
NSFileManager *manager = [[NSFileManager alloc] init];
NSFileManager *defaultManager = [NSFileManager defaultManager];

多線程

  • 線程生而不平等 : 主線程 & 后臺線程; 線程有優(yōu)先級的關(guān)系
  • 線程是一個(gè)被模擬出來的概念 : CPU 通過分配時(shí)間片模擬出多線程同時(shí)工作的狀態(tài)

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 將線程執(zhí)行的結(jié)果傳出
@interface MyThread : NSThread
@end
@implementation
- (void)main {
    // 在這里重寫線程實(shí)現(xiàn)的方法
    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 將線程狀態(tài)標(biāo)記成取消狀態(tài)
+ (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;

多線程優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn)
    • 提高 APP 實(shí)時(shí)響應(yīng)性
    • 充分利用計(jì)算資源
  • 缺點(diǎn)
    • 額外的系統(tǒng)開銷
    • 線程同步問題
    • 程序復(fù)雜度上升

多線程同時(shí)訪問資源

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

[_lock lock]; // 加鎖
// 這里執(zhí)行的代碼會把使用的成員變量鎖住
[_lock unlock]; // 解鎖

// 過程猜測: 使用 [_lock lock] 的時(shí)候, 如果 _lock 在鎖住狀態(tài), 則這個(gè)操作會一直等待, 
// 等到操作執(zhí)行完之后, _lock 被解鎖, 才能執(zhí)行下次加鎖操作
  • 使用 @synchronized
    • 簡單
    • 不需要自己 unlock, 不容易產(chǎn)生死鎖
@synchronized (self) {
    // 執(zhí)行操作, 這里的操作會被加鎖, 執(zhí)行完后退出 synchronized 則會被解鎖
}

死鎖狀態(tài)

GCD (Grand Central Dispatch)

// 隊(duì)列
dispatch_quequ_t

// 創(chuàng)建
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

// 創(chuàng)建
dispatch_semaphore_t
dispatch_semaphore_create(long value);

// 觸發(fā)信號量
long
dispatch_semaphore_signal(dispatch_semaphore_t dsema);

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

NSOperation

  1. 創(chuàng)建 NSOperationQueue : NSOperationQueue *queue = [[NSOperationQueue alloc] init];

  2. 創(chuàng)建 NSOperation 子類對象 : 重寫 NSOperation 任務(wù)執(zhí)行函數(shù)

    @interface  MyOperation : NSOperation
    @end
    @implementation MyOperation
    - (void)main {
        // 任務(wù)執(zhí)行函數(shù)
    }
    
  3. 將 NSOperation 的子類對象加入 NSOperationQueue : [queue addOperation:operation];

  4. 設(shè)置子類對象的 completionBlock, 在 block 中進(jìn)行剩余的 UI 操作

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    MyOperation *op = [[MyOperation alloc] init];
    __weak typeof(op) weakOp = op;
    op.completionBlock = ^(){
        // 執(zhí)行完畢之后需要進(jìn)行的操作
        // 在此的操作也是在子線程進(jìn)行的, 所以如果涉及到 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 支持取消
    • 標(biāo)記成取消狀態(tài), 當(dāng)執(zhí)行到這個(gè)任務(wù)的時(shí)候, 任務(wù)不會被執(zhí)行但是 completetionBlock 仍然會被執(zhí)行
  • 封裝, 能適應(yīng)更復(fù)雜的操作和提供更精細(xì)化的操控

NSOperation 等待

  • 創(chuàng)建兩個(gè) OperationQueue : uploadQueue, finalQueue
  • 在 uploadQueue 中添加所有的操作
  • 在 finalQueue 中添加后續(xù)的操作 finalOperation, 中間加入代碼
    • [uploadQueue waitUntilAllOperationsAreFinished];
    • finalOperation 會等待到所有的 uploadQueue 中任務(wù)執(zhí)行完然后執(zhí)行

使用 dependency 等待任務(wù)

@interface NSOperation : NSObject
- (void)addDependency:(NSOperation *)op; // 添加依賴任務(wù)
- (void)removeDependency:(NSOperation *)op; // 移除依賴任務(wù)
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
  • 循環(huán)依賴
    • 顯式循環(huán)依賴
    • 隱式循環(huán)依賴 : 串行隊(duì)列中, 前面的任務(wù)依賴于后面的任務(wù), 導(dǎo)致任務(wù)循環(huán)依賴

優(yōu)先級

@interface NSOperation : NSObject
@property NSOperationQueuePriority queuePriority;

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

RunLoop 常駐的主線程

  • 和線程一一對應(yīng), 每個(gè)線程有且只有一個(gè) RunLoop
  • 線程創(chuàng)建的時(shí)候并未有 RunLoop, 需要手動創(chuàng)建, 主線程除外
  • RunLoop 的創(chuàng)建發(fā)生在第一次獲取它的時(shí)候(單例)
  • 只能在線程內(nèi)部獲取對應(yīng)的 RunLoop (主線程 RunLoop 除外)
@interface NSRunLoop : NSObject
+ (NSRunLoop *)currentRunLoop;
+ (NSRunLoop *)mainRunLoop;

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

常駐的后臺線程

  • 將有繁重的操作的回調(diào)指定到固定的線程中執(zhí)行
  • perform selector 指定到固定的線程執(zhí)行

RunLoop Mode

  • NSDefaultRunLoopMode
  • NSRunLoopCommonModes
    • default mode
    • modal mode
    • tracking mode UITrackingRunLoopMode
@interface NSRunLoop : NSObject
- (void)addTimer:(NSTimer *)timer forMode:(NSString *);
@end

iOS 網(wǎng)絡(luò)基礎(chǔ)

NSURLRequest

/********** 創(chuàng)建請求 **********/
@interface NSURLRequest : NSObject
// 各種屬性都不可設(shè)置
+ (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
// ... 各種屬性都是可以設(shè)置的

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


/********** 發(fā)送請求 **********/
@interface NSURLConnection : NSObject // 逐步被廢棄, 推薦使用 NSURLSession
- (nullable instancetype)initWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate startImmediately:(BOOL)startImmediately;
- (void)start;
@end


/********** 接收響應(yīng) **********/
@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


/********** 獲取數(shù)據(jù) : 解析 JSON/XML 數(shù)據(jù) **********/
// 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

// 創(chuàng)建一個(gè)請求
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"];

// 發(fā)送請求
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection start];

// 接收響應(yīng)
- (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 數(shù)據(jù), 可能是 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;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末每篷,一起剝皮案震驚了整個(gè)濱河市霎匈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌千埃,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡磺陡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門漠畜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來币他,“玉大人,你說我怎么就攤上這事憔狞『ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵瘾敢,是天一觀的道長拍冠。 經(jīng)常有香客問我,道長簇抵,這世上最難降的妖魔是什么庆杜? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮碟摆,結(jié)果婚禮上晃财,老公的妹妹穿的比我還像新娘。我一直安慰自己典蜕,他們只是感情好断盛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布罗洗。 她就那樣靜靜地躺著,像睡著了一般钢猛。 火紅的嫁衣襯著肌膚如雪伙菜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天命迈,我揣著相機(jī)與錄音贩绕,去河邊找鬼。 笑死壶愤,一個(gè)胖子當(dāng)著我的面吹牛丧叽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播公你,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼假瞬!你這毒婦竟也來了陕靠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤脱茉,失蹤者是張志新(化名)和其女友劉穎剪芥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琴许,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡税肪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榜田。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片益兄。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖箭券,靈堂內(nèi)的尸體忽然破棺而出净捅,到底是詐尸還是另有隱情,我是刑警寧澤辩块,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布蛔六,位于F島的核電站,受9級特大地震影響废亭,放射性物質(zhì)發(fā)生泄漏国章。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一豆村、第九天 我趴在偏房一處隱蔽的房頂上張望液兽。 院中可真熱鬧,春花似錦你画、人聲如沸抵碟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拟逮。三九已至撬统,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敦迄,已是汗流浹背恋追。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罚屋,地道東北人苦囱。 一個(gè)月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像脾猛,于是被迫代替她去往敵國和親撕彤。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容