【资源共享的问题】
例如线程A与B均实现数字Num的加一操作,如果不加以限制,可能A和B先后拿到最初的Num,然后返回Num+1,无法实现Num+1之后再+1。
【互斥锁】
使用互斥锁(@synchronized)来解决,让线程A操作时锁住Num,不允许B的读写,直到A操作完并且写回后,再让B进行工作,再锁住Num,直到B操作完毕,再解开锁,类似于上厕所,在厕所内要锁门一样。
使用@synchronized(self){......}包装在内的为互斥锁的作用范围,会严重降低效率。因此应尽可能的减小范围。不推荐使用。
【原子锁】
一般的成员变量都写为nonatomic,即为非原子锁,如果写为atomic就是原子锁,这样的变量只支持多读单写,原子锁限制了写入,对读取没有影响,原子锁的性能影响较小。
【并发编程的主要目的】
并发编程最主要的目的是提高性能,让更多的代码同时运行,提高整体性能。
由于手机端一般不必解决资源抢夺问题,因此一般不会涉及到资源抢夺。
Tip:线程的休眠方法:
[NSThread sleepForTimeInterval:0.1f];
Tip:注意多线程开发永远不要相信一次运行结果。
【UI都在主线程的原因】
出于性能考虑,UIKit中绝大多数的类都不是线程安全的,因此,官方要求更新UI的相关操作应该在主线程中执行。
【单例设计模式】
如UIApplication、音频播放可以保证音乐一直播放,不随着视图控制器的改变而受到影响。
再如一些硬件资源,例如加速计、屏幕(UIScreen mainScreen),带有sharedXxx和mainXxx关键字的常常都是单例。
单例在面试中可能会要求手写。
单例的实现步骤:
1.任何对象的alloc方法都会最终调用allocWithZone来实例化,因此重写allocWithZone来实现初始化结果的唯一性。
2.在allocWihtZone方法中,利用dispatch_once保证调用父类实现初始化的代码只被执行一次,最后返回对象本身。
为了保证单例不被销毁,使用一个静态指针,静态指针存放在堆中,直到应用程序终止后才会被销毁。
// 在iOS中,所有对象的内存空间分配,最终都会调用allocWithZone方法 // 制作单例,需要重写此方法 + (id)allocWithZone:(struct _NSZone *)zone{ static DemoObj *instance; // dispatch_once_t是线程安全的 static dispatch_once_t onceToken; // dispatch_once宏可以保证快代码的指令只被执行一次 dispatch_once(&onceToken, ^{ // 只会被执行一次 instance = [super allocWithZone:zone]; }); return instance; }
3.写一个sharedXxx方法用于返回单例对象,由于alloc已经唯一,因此直接在shared方法中返回一个初始化的实例即可:
+ (instancetype)sharedDemoObj{ return [[self alloc] init]; }
单例的缺点:单例对象一旦被创建,对象指针会存放在静态区,在堆中分配空间,会在应用程序终止后才会释放。
另一种单例的实现:注意这样是不好的。
static DemoObj2 *instance; @implementation DemoObj2 + (instancetype)sharedDemoObj2{ if (!instance) { instance = [[self alloc] init]; } return instance; } @end
如果仅仅是实现了sharedXxx方法,如果仍使用alloc init仍可以得到多个实例。
如果有两个线程同时实例化,很可能创建出两个实例来(是线程不安全的)。