屬性關鍵字
讀寫權限
- readonly
- readwrite(默認)
原子性
OC中的屬性可以修飾成nonatomic和atomic缔杉,即原子和非原子屬性久窟。atomic屬性設計的出發(fā)點是保證多線程下使用屬性的安全性妈拌,
- atomic(默認)
atomic
修飾的屬性可以保證賦值和獲取值(對成員屬性的直接獲取和賦值著蛙,并不代表操作和訪問),線程安全概荷。
假如一個atomic
修飾的的數(shù)組,對數(shù)組進行賦值和獲取可以保證線程安全工闺,如果對數(shù)組進行操作乍赫,比如給數(shù)組添加對象或移除對象元素瓣蛀,則不在atomic
的負責范圍內(nèi),沒辦法保證數(shù)組的線程安全雷厂。
使用atomic修飾的屬性并不會線程安全
源碼objc4-750.1
void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot]; //自旋鎖
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
從使用PropertyLocks
數(shù)組中的一個給寫操作上鎖惋增,在賦完值之后進行解鎖操作。
也就是說改鲫,atomic
只是在 setter方法 中加鎖诈皿,當多個線程同時寫操作時,要進行排隊像棘。A線程對屬性進行寫操作稽亏,B線程不可以對該屬性進行寫操作,只有等A線程訪問結束缕题,B線程才能操作截歉。問題在于,當A線程再想對屬性進行讀操作烟零,讀取的是B線程寫的數(shù)據(jù)瘪松,這就破壞了線程安全,。如果再有C線程在A線程讀操作前Release這個屬性锨阿,那么程序就會Crash.
綜上宵睦,atomic
操作是原子性的,它只保證了屬性的setter和getter時的線程安全墅诡,并不能保證屬性線程安全壳嚎,atomic
的使用只是更好的降低了線程出錯的可能性。
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
- nonatomic
atomic 與 nonatomic 的區(qū)別如下:
-
atomic
與nonatomic
的本質區(qū)別其實也就是在setter
方法上的操作不同,atomic
保證了getter
和setter
存取方法的線程安全末早,兩者都不能保證整個對象是線程安全的烟馅。 -
nonatomic
的速度要比atomic
的快。
鑒于以上兩點荐吉,大部分使用的是nonatomic
這個屬性焙糟。
那么問題來了,Apple為什么只給setter和getter方法加鎖而不直接使用@synchronized加鎖呢样屠?
看看 synchronized
操作的源碼穿撮, 簡單的一個注解,其實做了許多事情痪欲,每個@synchronized()
代碼塊悦穿,使用了 objc_sync_enter
、objc_sync_exit
和id2data
三個加解鎖序列业踢,而這種作法相比使用一個atomic
來修飾栗柒,只給setter
、getter
方法加鎖的方式 在性能上要慢很多。讀寫操作一個屬性時瞬沦,通常需要很快來完成太伊,atomic
的方式要適合這項工作。
在必要時逛钻,可以使用@synchronized
僚焦,但是在保證多線程操作在發(fā)生錯誤的時候,不會發(fā)生dead lock曙痘。
引用計數(shù)
retain/strong(修飾對象的)
assign/unsafe_unretained(基本數(shù)據(jù)類型或對象)
assign與weak的區(qū)別有哪些芳悲?
assign
修飾基本數(shù)據(jù)類型,如int
,BOOL
等assign
修飾對象類型時边坤,不改變其引用計數(shù)名扛。-
會產(chǎn)生懸垂指針。
assign
修飾的對象在被釋放之后茧痒,其assign
指針仍然指向原對象內(nèi)存地址肮韧,這個時候如果通過assign
指針繼續(xù)訪問對象,可能會由于懸垂指針的原因導致內(nèi)存泄漏文黎,或程序異常惹苗。 weak
不改變被修飾對象的引用計數(shù)所指對象在被釋放之后會自動置為
nil
weak
之修飾對象
copy
- 可變對象的
copy
和mutableCopy
都是深拷貝殿较。 - 不可變對象的
copy
是淺拷貝耸峭,mutableCopy
是深拷貝。 -
copy
方法返回的都是不可變對象淋纲;
深拷貝與淺拷貝
淺拷貝就是對內(nèi)存地址的復制劳闹,讓目標對象指針和源對象指向同一片內(nèi)存空間。
- 會增加對象的引用計數(shù)
- 沒有發(fā)生新的內(nèi)存分配洽瞬。
深拷貝讓目標對象指針和源對象指針指向兩片內(nèi)容相同的內(nèi)存空間本涕。
- 不會增加對象的引用計數(shù)
- 產(chǎn)生了新的內(nèi)存空間分配
@property(copy) NSMutableArray *array;
如果像上面那樣聲明一個成員屬性會導致什么問題?
如果賦值過來的是
NSMutableArray
,copy之后是NSArray
伙窃。-
如果賦值過來的是
NSArray
,copy之后是NSArray
菩颖。所以無論被賦值過來的是
NSMutableArray
,還是NSArray
copy的結果都是不可變對象,由于原來的屬性被聲明為NSMutablArray
就不可避免的有調用方調用其進行元素的添加與移除为障,此時由于copy的結果是NSArray
晦闰,調用其子類方法的添加與移除會造成程序的崩潰。
OC語言筆試題
MRC下如何重寫retain
修飾變量的setter方法?
@property(nonatomic, retain) id obj;
- (void)setObj:(id)obj
{
if(_obj != obj) { //防止提早釋放了對象
[_obj release];
_obj = [obj retain];
}
}
請簡述分類實現(xiàn)原理鳍怨?
KVO的實現(xiàn)原理是怎樣的呻右?
能否為分類添加成員變量?