本文探索常用鎖以及@synchronized
底層的原理。
鎖的分類
在開發(fā)中奕谭,使用最常見的恐怕就是@synchronized
(互斥鎖)稳强、NSLock
(互斥鎖)厂抖、以及dispatch_semaphore
(信號(hào)量)溯街。其實(shí)還有許多種匹中,總分類有:互斥鎖、自旋鎖朱浴,細(xì)分之下多出了: 讀寫鎖吊圾、遞歸鎖达椰、條件鎖、信號(hào)量街夭,后三者是對(duì)基本鎖的上層封裝砰碴。先介紹幾個(gè)概念躏筏。
【自旋鎖】是用于多線程同步的一種鎖板丽,線程反復(fù)檢查鎖變量是否可用(即可重入特性)。由于線程在這一過程中保持執(zhí)行趁尼,因此是一種忙等待埃碱。一旦獲取了自旋鎖,線程會(huì)一直保持該鎖酥泞,直至顯式釋放自旋鎖砚殿。 自旋鎖避免了進(jìn)程上下文的調(diào)度開銷,因此對(duì)于線程只會(huì)阻塞很短時(shí)間的場(chǎng)合是有效的芝囤。
【互斥鎖】是一種用于多線程編程中似炎,防止兩條線程同時(shí)對(duì)同一公共資源(比如全局變量)進(jìn)行讀寫的機(jī)制。該目的通過將代碼切片成一個(gè)一個(gè)的臨界區(qū)而達(dá)成悯姊。
遞歸鎖(recursive_mutext_t)是一種特殊的互斥鎖羡藐。
【讀寫鎖】是計(jì)算機(jī)程序的并發(fā)控制的一種同步機(jī)制(也稱“共享-互斥鎖”、多讀-單寫鎖) 用于解決多線程對(duì)公共資源讀寫問題悯许。讀的操作可并發(fā)重入仆嗦,寫操作是互斥的。 讀寫鎖通常用互斥鎖先壕、條件變量瘩扼、信號(hào)量實(shí)現(xiàn)。
【信號(hào)量】是一種更高級(jí)的同步機(jī)制垃僚,互斥鎖可以說是semaphore
在僅取值0/1時(shí)的特例集绰。信號(hào)量可以有更多的取值空間,用來實(shí)現(xiàn)更加復(fù)雜的同步谆棺,而不單單是線程間互斥栽燕。
【條件鎖】:條件鎖就是條件變量,當(dāng)進(jìn)程的某些資源要求不滿足時(shí)就進(jìn)入休眠包券,即鎖住了纫谅,當(dāng)資源被分配到了,條件鎖打開了溅固,進(jìn)程繼續(xù)運(yùn)行付秕。
對(duì)應(yīng)有以下鎖:
- OSSpinLock(自旋鎖)
- dispatch_semaphone(信號(hào)量)
- pthread_mutex(互斥鎖)
- NSLock(互斥鎖)
- NSCondition(條件鎖)
- os_unfair_lock (互斥鎖)
- pthread_mutex(recursive 互斥遞歸鎖)
- NSRecursiveLock(遞歸鎖)
- NSConditionLock(條件鎖)
- synchronized(互斥遞歸鎖)
OSSpinLock(自旋鎖)
- 與互斥鎖(阻塞-睡眠)不同,自旋鎖加鎖后是進(jìn)入忙等狀態(tài)侍郭。
- 如果共享數(shù)據(jù)已經(jīng)有其他線程加鎖了询吴,線程會(huì)以忙等的方式等待鎖掠河,一旦被訪問的資源被解鎖,則等待資源的線程會(huì)立即執(zhí)行猛计。
OSSpinLock效率很高唠摹,但是已不再安全。如果一個(gè)低優(yōu)先級(jí)的線程獲得鎖并訪問共享資源奉瘤,這時(shí)一個(gè)高優(yōu)先級(jí)的線程也嘗試獲得這個(gè)鎖勾拉,它會(huì)處于 spin lock 的忙等狀態(tài)從而占用大量 CPU。此時(shí)低優(yōu)先級(jí)線程無法與高優(yōu)先級(jí)線程爭(zhēng)奪 CPU 時(shí)間盗温,從而導(dǎo)致任務(wù)遲遲完不成藕赞、無法釋放 lock。
ibireme 大神--<不再安全的 OSSpinLock>
各類型鎖的性能
所以蘋果已經(jīng)推薦使用os_unfair_lock
卖局。
- os_unfair_lock基本使用
關(guān)于os_unfair_lock
是蘋果在iOS10之后推出斧蜕,它屬于互斥鎖,os_unfair_lock
加鎖會(huì)讓等待的線程進(jìn)入休眠狀態(tài)砚偶,而不是忙等批销。這樣就提高了安全也降低了性能損耗。
#import <os/lock.h>
// 創(chuàng)建一個(gè) os_unfair_lock_t 鎖
os_unfair_lock_t unfairLock;
// 先分配此類型的變量并將其初始化為OS_UNFAIR_LOCK_INIT
unfairLock = &(OS_UNFAIR_LOCK_INIT);
// 嘗試加鎖染坯,返回YES or NO
os_unfair_lock_trylock(unfairLock)
// 加鎖
os_unfair_lock_lock(unfairLock);
// 解鎖
os_unfair_lock_unlock(unfairLock);
dispatch_semaphone(信號(hào)量)
信號(hào)量適用于異步線程同步操作的場(chǎng)景均芽。
// 創(chuàng)建使用
dispatch_semaphore_create(long value); // 創(chuàng)建信號(hào)量
dispatch_semaphore_signal(dispatch_semaphore_t deem); // 發(fā)送信號(hào)量
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); // 等待信號(hào)量
// ??注意: 發(fā)送信號(hào)量和信號(hào)等待是成對(duì)出現(xiàn)
// 常見使用場(chǎng)景
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{ // ①
NSLog(@"任務(wù)1:%@",[NSThread currentThread]);
dispatch_semaphore_signal(sem); // ③
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // ②
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)2:%@",[NSThread currentThread]);
dispatch_semaphore_signal(sem); // ⑤
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // ④
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)3:%@",[NSThread currentThread]); // ⑥
});
// 執(zhí)行順序:① - ② - ③ - ④ - ⑤ - ⑥
}
通過控制信號(hào)量通過數(shù),就可實(shí)現(xiàn)鎖的功能酒请。
pthread_mutex(互斥鎖)
- 阻塞線程并
sleep
(加鎖)骡技,加鎖過程中切換上下(主動(dòng)出讓時(shí)間片,線程休眠羞反,等待下一次喚醒)布朦、cpu的搶占、信號(hào)的發(fā)送等開銷昼窗。 - 如果共享數(shù)據(jù)已經(jīng)有其他線程加鎖了是趴,線程會(huì)進(jìn)入休眠狀態(tài)等待鎖。一旦被訪問的資源被解鎖澄惊,則等待資源的線程會(huì)被喚醒唆途。
- 互斥鎖范圍,應(yīng)該盡量械肛搬;鎖定范圍越大,效率越差毕贼。
- 能夠給任意NSObject對(duì)象加鎖温赔。
加解鎖流程:sleep(加鎖) -> 出讓時(shí)間片 -> 線程休眠 -> 等待喚醒 -> running(解鎖)
時(shí)間?(quantum):系統(tǒng)給每個(gè)正在運(yùn)行的進(jìn)程或線程微觀上的一段CPU時(shí)間。
// 導(dǎo)入互斥鎖頭文件--C語(yǔ)言
#import <pthread.h>
// 可添加成員變量
pthread_mutex_t mutex;
- (void)myfun
{
pthread_mutex_init(&mutex, NULL);
}
- (void)MyLockingFunction
{
pthread_mutex_lock(&mutex);
// Do something.
pthread_mutex_unlock(&mutex);
}
- (void)dealloc
{
// 不用要釋放掉
pthread_mutex_destroy(&mutex);
}
// 這只是簡(jiǎn)單使用鬼癣,具體還需針對(duì)進(jìn)行錯(cuò)誤代碼處理
互斥鎖 vs 自旋鎖
相同:都能保證同一時(shí)間只有一個(gè)線程訪問共享資源陶贼。都能保證線程安全啤贩。
不同:
- 互斥鎖:如果共享數(shù)據(jù)已經(jīng)有其他線程加鎖了,線程會(huì)進(jìn)入休眠狀態(tài)等待鎖拜秧。一旦被訪問的資源被解鎖痹屹,則等待資源的線程會(huì)被喚醒。
- 自旋鎖:如果共享數(shù)據(jù)已經(jīng)有其他線程加鎖了枉氮,線程會(huì)以忙等的方式等待鎖志衍,一旦被訪問的資源被解鎖,則等待資源的線程會(huì)立即執(zhí)行嘲恍。
NSLock(互斥鎖)
NSLock是對(duì)底層pthread_mutex
的封裝足画。一般使用有:
self.lock = [[NSLock alloc] init];
[self.lock tryLock]; // 嘗試加鎖雄驹;返回YES or NO
[self.lock lock]; // 加鎖
[self.lock unlock]; // 解鎖
底層原理
NSLock
是Foundation
下的佃牛,閉源則源碼不可見。借助Swift的Foundation
可以看看同集成NSLocking
的NSLock
在底層做了什么操作医舆。
- 調(diào)用必須初始化俘侠;而底層則直接調(diào)用了互斥鎖
pthread_mutex_init
.(可以知道性能相近的原因了)
- 調(diào)用必須初始化俘侠;而底層則直接調(diào)用了互斥鎖
- 底層實(shí)現(xiàn)也是調(diào)用了
pthread_mutex
的lock
和unlock
.即就是對(duì)pthread_mutex
的封裝。
- 底層實(shí)現(xiàn)也是調(diào)用了
在Apple官方文檔中指出
Warning
The NSLock class uses POSIX threads to implement its locking behavior. When sending an
unlock message to an NSLock object, you must be sure that message is sent from the
same thread that sent the initial lock message. Unlocking a lock from a different thread can result in undefined behavior.
Tra:本NSLock類使用POSIX線程執(zhí)行其鎖定行為蔬将。向NSLock對(duì)象發(fā)送解鎖消息時(shí)爷速,必須確保該消
息是從發(fā)送初始鎖定消息的同一線程發(fā)送的。從其他線程解鎖鎖可能導(dǎo)致未定義的行為霞怀。
所有它僅限用于同一線程中惫东,且也不應(yīng)使用此類來實(shí)現(xiàn)遞歸鎖。lock
在同一線程上兩次調(diào)用該方法將永久鎖定您的線程毙石。原因是加鎖還未解鎖又再一次加鎖廉沮,一直在加鎖就會(huì)陷入死鎖狀態(tài)。如下:
NSLock *lock = [[NSLock alloc] init];
for (int i= 0; i<50; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testMethod)(int);
testMethod = ^(int value){
[lock lock];
if (value > 0) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
};
testMethod(10);
[lock unlock];
});
}
可以使用NSRecursiveLock
來實(shí)現(xiàn)遞歸鎖徐矩,也可使用@synchronized
替代處理滞时。
@synchronized
@synchronized
是開發(fā)中用的非常廣泛的一種鎖,目的就是防止不同的線程同時(shí)執(zhí)行同一段代碼滤灯。但是就其性能而言坪稽,可謂慘不忍睹。常言存在即合理鳞骤,廣泛使用窒百,面試中常常被提及,就需要探索一下其底層原理豫尽。
首先確定下研究方法:1. 匯編篙梢;2. Clang。
有這樣一個(gè)例子拂募,「onePx」奶茶店生意很好庭猩,奶茶就剩20杯的量窟她,三個(gè)窗口賣(類似三條條線程),這還有越賣越多情況蔼水,就不符合逾期了震糖。這就多線程對(duì)同一資源訪問,發(fā)生了數(shù)據(jù)錯(cuò)亂趴腋。
當(dāng)然了吊说,主題是鎖,就通過加鎖即可解決优炬。譬如加一個(gè)
@synchronized
颁井,就完美控制。匯編
在@synchronized
打下一個(gè)端點(diǎn)蠢护,打開匯編雅宾,Xcode菜單欄,Debug -> Debug Workflow -> Always Show Disassembly
通過匯編可以窺見一二葵硕,在加鎖和無鎖的情況下有很大區(qū)別眉抬,執(zhí)行流程變得更加復(fù)雜,且多出兩個(gè)關(guān)鍵方法:objc_sync_enter懈凹、objc_sync_exit
蜀变,這就是@synchronized
的進(jìn)出口方法。
Clang
在main.m中編寫一個(gè)@synchronized
方法
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
@synchronized (appDelegateClassName) {
}
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
然后對(duì)其Clang指令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
編譯出已C++文件介评,查看底層
由圖中可驗(yàn)證匯編形式下库北,
@synchronized
的底層會(huì)調(diào)用objc_sync_enter、objc_sync_exit
们陆。如果捕獲到異常寒瓦,就把異常拋出。
objc_sync_enter
在之前的工程中下一個(gè)方法為objc_sync_enter
的符號(hào)斷點(diǎn)棒掠,我們可以知道底層源碼是在libobjc.A.dylib
中孵构。也可以在@synchronized
處下斷點(diǎn),進(jìn)入?yún)R編然后調(diào)試到objc_sync_enter
烟很,點(diǎn)擊底部調(diào)試菜單欄step into
進(jìn)入objc_sync_enter
的深一層匯編颈墅,也可以知道其歸屬于libobjc.A.dylib
范疇。
其實(shí)從這里就可以知道它在底層大致做了什么雾袱,做一個(gè)等值判斷恤筛,根據(jù)判斷結(jié)果,它會(huì)調(diào)用
id2Data
的一個(gè)方法芹橡,然后再會(huì)調(diào)用一個(gè)os_unfair_recursive_lock_lock_with_options
毒坛;否則就跳轉(zhuǎn)調(diào)試超父類,調(diào)用一個(gè)方法objc_sync_nil
。嚴(yán)謹(jǐn)一點(diǎn)還是要走底層代碼摸索一波煎殷。
打開一份前面文章分析用過的objc源碼屯伞,我們可以查找到對(duì)應(yīng)的源碼
// Begin synchronizing on 'obj'. 開始同步
// Allocates recursive mutex associated with 'obj' if needed.
// 如果需要,分配與“ obj”關(guān)聯(lián)的遞歸互斥體豪直。 這里可以知道劣摇,它是一把遞歸互斥鎖,具體看底層弓乙。
// Returns OBJC_SYNC_SUCCESS once lock is acquired.
// 成功時(shí)返回一個(gè) OBJC_SYNC_SUCCESS
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
// 先判空
if (obj) {
// id2data -> 關(guān)鍵方法末融。obj 不為空,從id2Data 獲取一個(gè)SyncData類型數(shù)據(jù)暇韧,然后加鎖勾习。
SyncData* data = id2data(obj, ACQUIRE);
ASSERT(data);
// 加鎖
/**
mutex 類型為 recursive_mutex_t;
*/
data->mutex.lock();
} else {
// @synchronized(nil) does nothing 如果加鎖傳入的obj為空懈玻,什么也不做
// 如果obj 為空巧婶,報(bào)以奔潰 objc_sync_nil()
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
- SyncData結(jié)構(gòu) + SyncList結(jié)構(gòu)
typedef struct alignas(CacheLineSize) SyncData {
struct SyncData* nextData; // SyncData -> SyncData ,不斷的鏈接。這證明是一個(gè)鏈表結(jié)構(gòu)
DisguisedPtr<objc_object> object; //通過運(yùn)算使指針隱藏于系統(tǒng)工具酪刀,同時(shí)保持指針的能力粹舵,其作用是通過計(jì)算把保存的 T 的指針隱藏起來,實(shí)現(xiàn)指針到整數(shù)的映射骂倘。
int32_t threadCount; // number of THREADS using this block。該代碼塊的線程數(shù)
recursive_mutex_t mutex; // 證明其底部就是遞歸互斥
} SyncData;
struct SyncList {
SyncData *data;
spinlock_t lock;
constexpr SyncList() : data(nil), lock(fork_unsafe_lock) { }
};
// Use multiple parallel lists to decrease contention among unrelated objects.使用多個(gè)并行列表以減少不相關(guān)對(duì)象之間的爭(zhēng)用巴席。
// 哈希出對(duì)象的數(shù)組下標(biāo)历涝,然后取出數(shù)組對(duì)應(yīng)元素的 lock 或 data
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap<SyncList> sDataLists;
SyncData
做為一個(gè)一個(gè)的節(jié)點(diǎn),依次存儲(chǔ)漾唉,每個(gè) SyncList 結(jié)構(gòu)體都有個(gè)指向 SyncData
節(jié)點(diǎn)鏈表頭部的指針荧库,也有一個(gè)用于防止多個(gè)線程對(duì)此列表做并發(fā)修改的鎖。類似SyncData -> SyncData -> SyncData ...
赵刑,它是鏈表形式存儲(chǔ)分衫,不同的并行列表間互相不對(duì)數(shù)據(jù)進(jìn)行訪問。
鏈表的底層是通過哈希算法來存儲(chǔ)的般此,即StripedMap
底層就是是將對(duì)象指針在內(nèi)存的地址轉(zhuǎn)化為無符號(hào)整型,通過算法((addr >> 4) ^ (addr >> 9)) % StripeCount
, 來獲取下標(biāo)indexForPointer
蚪战。
SyncList示意圖
-
obj == nil時(shí)啥也不做
obj = nil --> do nothing
無論是objc_sync_enter
還是objc_sync_exit
都調(diào)用了一個(gè)關(guān)鍵方法id2Data()
,則我們的側(cè)重點(diǎn)就是在里面的實(shí)現(xiàn)铐懊。
id2Data
- 第一個(gè)情況 : SUPPORT_DIRECT_THREAD_KEYS = 1邀桑;即為快速緩存查找
線程緩存池中快速查找
- 在線程緩存池中進(jìn)行快速查找對(duì)象,獲取鎖的數(shù)量科乎,再做一些異常判斷壁畸;
- 判斷
why
,如果類型為ACQUIRE
,則使得lockCount
加1,再存儲(chǔ)下來捏萍;
如果類型為RELEASE
太抓,則使得lockCount
減1,再存儲(chǔ)下來令杈;
如果類型為CHECK
,啥也不做腻异。
- 第二個(gè)情況:SyncCache緩存查找
SyncCache緩存查找
SyncCache結(jié)構(gòu),與tls線程緩存相似这揣,它也是鏈?zhǔn)酱鎯?chǔ)悔常。
typedef struct {
SyncData *data;
unsigned int lockCount; // number of times THIS THREAD locked this block 線程加鎖次數(shù)
} SyncCacheItem;
typedef struct SyncCache {
unsigned int allocated;
unsigned int used;
SyncCacheItem list[0];
} SyncCache;
有所不同的是,它如果有多條線程時(shí)给赞,就會(huì)有多個(gè)這樣的list机打。
這個(gè)情況下就是在SyncCache的緩存列表里,不同線程間片迅,查找是否有匹配對(duì)象残邀,如果找到匹配對(duì)應(yīng)的類型,進(jìn)行lockCount
加和減操作柑蛇。
- 第一次進(jìn)來
首次進(jìn)入鎖模塊
先在線程池中遍歷獲取p->nextData
,通過p =*listp
取出第一個(gè)SyncData
數(shù)據(jù), 判斷p->object
是否為傳入object
, 如果相等芥挣,則存儲(chǔ)下object
的持有者SyncData
的p
;跳轉(zhuǎn)到done
耻台,通過判斷fastCacheOccupied
, YES時(shí)存儲(chǔ)到快速線程緩存中空免;否則存儲(chǔ)到線程緩存(SyncCache)中。
【總結(jié)】
- 首次進(jìn)來: 沒有鎖盆耽,
threadCount = 1蹋砚,lockCount = 1
, 存到tls_set_direct
中摄杂; - 不是第一次進(jìn)來坝咐,是在
tls鏈表
進(jìn)行快速緩存查找的,它們是在同一個(gè)線程進(jìn)行lockCount
加析恢,并且將result
存到tls_set_direct
; - 不是第一次但是在SyncCache中查找的墨坚,則可能在一個(gè)線程或多個(gè)線程中遍歷SyncCacheItem類型的list單向鏈表,查找SyncData映挂,查找到也會(huì)對(duì)
lockCount
加操作泽篮。
objc_sync_exit
// End synchronizing on 'obj'.
// Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, RELEASE);
if (!data) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
} else {
bool okay = data->mutex.tryUnlock();
if (!okay) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
}
}
} else {
// @synchronized(nil) does nothing
}
return result;
}
這解鎖就是反向操作了,這可不是像某些人反向吸Y那樣子袖肥。??
它要判斷obj
咪辱,然后在id2Data
里面做release操作,使得lockCount減少椎组;
如果拿到的非空data閑嘗試解鎖油狂,解鎖成功的即返回result;解鎖失敗的,及時(shí)報(bào)錯(cuò)专筷。
【@synchnized 坑點(diǎn)】
坑點(diǎn)1:經(jīng)過以上的分析弱贼,@synchnized
慢的原因就非常清楚了,它的加鎖解鎖都經(jīng)過了一些列的增刪改查再加緩存磷蛹,鏈表接口的存取都會(huì)影響速度吮旅。
坑點(diǎn)2:也是一個(gè)面試題
- (void)testSynchronized {
for (int i = 0; i < 10000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.dataSources = [NSMutableArray array];
});
}
}
執(zhí)行之后會(huì)卡死∥犊龋可以通過開啟
再次運(yùn)行就可以看到奔潰庇勃。原因是在反復(fù)初始化,調(diào)用
setter
retain 新值槽驶,釋放舊值责嚷。線程不斷的release舊值,導(dǎo)致了野指針掂铐。嘗試鎖一下
- (void)testSynchronized {
for (int i = 0; i < 10000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@synchronized (self.dataSources) {
self.dataSources = [NSMutableArray array];
}
});
}
}
但是會(huì)出現(xiàn)同樣的奔潰罕拂,原因是:self.dataSources
的生命周期,它是會(huì)被釋放的全陨,釋放后為nil
爆班,在前面分析中就可以知道,如果加鎖對(duì)象為nil辱姨,則在「鎖」內(nèi)就do nothing
柿菩。
所以@synchnized
一般鎖self,保證鎖住的objc的生命周期未結(jié)束。但亦不能一直鎖self
炮叶,在底層objc_sync_enter
的時(shí)候碗旅,self
的鏈表會(huì)很多,就會(huì)導(dǎo)致鏈表查詢很繁瑣镜悉,性能降低更加明顯。
補(bǔ)充:atomic & nonatomic
atomic
- atomic 原?屬性(線程安全)医瘫,針對(duì)多線程設(shè)計(jì)的侣肄,需要消耗?量的資源
- atomic 本身就有?把鎖(?旋鎖)
- 保證同?時(shí)間只有?個(gè)線程能夠?qū)?,但是同?個(gè)時(shí)間多個(gè)線程都可以取值醇份。(單寫多讀:?jiǎn)蝹€(gè)線程寫?稼锅,多個(gè)線程可以讀取)
nonatomic
- nonatomic ?原?屬性
- nonatomic:?線程安全僚纷,適合內(nèi)存?的移動(dòng)設(shè)備矩距。
屬性應(yīng)都聲明為 nonatomic
;
盡量避免多線程搶奪同?塊資源怖竭;
盡量將加鎖锥债、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理,減?移動(dòng)客戶端的壓?。