沙盒模型
特点
- 安全
- 隐私
- 整洁
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
创建 NSOperationQueue :
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
-
创建 NSOperation 子类对象 : 重写 NSOperation 任务执行函数
@interface MyOperation : NSOperation @end @implementation MyOperation - (void)main { // 任务执行函数 }
将 NSOperation 的子类对象加入 NSOperationQueue :
[queue addOperation:operation];
-
设置子类对象的
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;