谈到属性的原子性,即操作读写的原子性(安全的,单一的,完整的) 可以这么理解。那有什么方法可以做到?自然,我们想到了锁的概念。

锁的种类

我们在开发Apps的时候,经常会用到多线程。而锁的概念可以抽象成线程同步。主要作用即保护授权某段代码块的原子性执行。iOS中锁有很多种类:

  • 信号量 Semaphore (允许N个有限线程在某一时间执行指定代码块)
  • 线程锁 Mutex 保证在某一时间内执行某代码块只有一个线程进入。
  • 自璇锁 Spinlock 指的是一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到的情况下会循环等待,不断的判断。
  • 读写锁 Read-write-lock 并发只读,串行写。
  • 递归锁 Recursive-lock 这个锁可以被同一线程多次请求,而不会引起死锁。它主要是用在循环或递归操作中。

Grand Central Dispatch (GCD),CPU多核处理,线程间异步高效地执行任务。在GCD的管理下,只需提供DispatchWorkItem作为单个任务,本质上是个swift 闭包。这些任务项item由GCD根据一定地规则排队自动执行。通过 queue.async(execute: workItem)。通过GCD可设置任务的优先级,以及执行顺序。
几个任务项也可以作为一个group, 通过DispatchGroup组织多任务执行,最后将所有处理完毕的结果作为单一整体汇总。
队列Queue管理着任务项的执行,可串行也可并发。串行队列一次执行一个任务,并发队列无需等待执行中的任务结束才开启下一个任务(即可同时执行多项任务)。两种队列都按照先进先出(FOFO)执行。
内部,有GCD线程池服务所有的队列。当某任务执行完成后线程即销毁。当所有的线程属于busy状态,新的线程需要暂时挂起。GCD预备了五种队列可直接使用。一种串行队列:主队列。四种异步队列分别权限为:hign,default,low,background。

Swift与OC最大的区别就是新增了值类型Struct, let变量修饰后无法再次被赋值实现真正的immutable,也就是说一旦赋值不能再次修改。虽然OC中定义NSArray *arr 也是一个immutable,但是arr对象指针指向还可以被再次重新赋值。OC是一门运行时语言,arr所指向的变量都是运行时获取对象。OC中使用immutable不直接等同于线程安全。Objective-C中存在深拷贝、浅拷贝,即使你调用一个[NSMutableArray Copy]得到的NSArray也不代表这个数组中的对象都是经过深拷贝。

我们先看一下如下代码会造成线程安全问题?:

1
2
3
if (self.xxx) {
[self.dict setObject:@"ah" forKey:self.xxx];
}

在单线程模式下肯定是没啥问题,但在多线程情况下比如线程A,线程B两者都同时进入了if条件,当A正要执行set操作,B已经对self.dict = nil。那么线程A肯定是不能设置成功的。
还有另外一种情况,当我们的数据资源比如集合,多线程对资源进行争夺。如添加和移除操作。为了保证最终结果的准确性,我们通常会给操作代码加锁。
1
2
3
OSSpinLock(&_lock);
[self.array addObject:@"data1"];
OSSpinUnlock(&_lock);

ios线程形式有三种。 ios4后,苹果改用GCD多线程。线程可以用来干什么呢?多线程可以用来后台加载图片更新主线程UI,或者请求网络数据等等。

Thread

使用 NSThread 来创建线程有两种方法:

1
2
3
4
5
6
7
8
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

NSThread* myThread = [[NSThread alloc] initWithTarget:self

selector:@selector(myThreadMainMethod:)

object:nil];
[myThread start]; // Actually create the thread