OC的十萬個為什么
為什么atomic 無法保證線程安全
Atomic 只能保證單步操作的原子性位隶。因此帜讲,對于簡單的賦值或者讀取操作,atomic還是可以保證該操作的完整性涝缝。但是台囱,一旦涉及到多步驟操作淡溯,還是需要lock等其他的同步機制來確保線程安全。
實例: @peroperty(atomic, strong) NSMutableArray * array;
array = [NSMutableArray array]; //線程安全
[array addObject:dummyObject];//線程不安全玄坦,
在讀取array后血筑,執(zhí)行addObject 的過程中绘沉,array所指向的object 可能已經(jīng)在其他地方被釋放了.
而在實際應(yīng)用中煎楣,大部分操作都是多步驟操作,atomic可以在一定程度上減少crash的幾率车伞,從而掩蓋多線程問題择懂,但是卻無法從根本上解決線程安全問題。
使用 atomic 一定是線程安全的嗎?
答案很明顯另玖。不是,atomic 的本意是指屬性的存取方法是線程安全的,并不保證整個對象是線程安全的困曙。
聲明一個 NSMutableArray 的原子屬性 stuff,此時 self.stuff 和 self.stuff =othersulf 都是線程安全的表伦。但是,使用[self.stuff objectAtIndex:index]就不是線程安全的,需要用互斥鎖來保證線程安全性。
為什么說@sychronized(self)的性能差
@sychronized 所包含的代碼片段 一次只允許一個線程執(zhí)行慷丽,同時又會阻塞調(diào)用線程蹦哼, 類似上述表格中Serial queue + dispatch sync 的組合 。一旦這些代碼片段同時被多個線程訪問要糊,就會對性能造成較大的影響
為什么dispatch_sync在主線程會死鎖
sync會阻塞當(dāng)前的提交線程纲熏。
原因是:在串行隊列中,第二個同步線程要執(zhí)行锄俄,必須等待第一個同步線程執(zhí)行完成后才可進行局劲,但是第一個同步線程要執(zhí)行完又得等待第二個同步線程執(zhí)行完,因為第二個同步線程嵌套在第一個同步線程里奶赠,這就造成了兩個同步線程互相等待鱼填,即死鎖。
特別強調(diào):是在串行隊列里毅戈!
為什么dispatch_sync在主線程會死鎖
關(guān)于dispatch_sync死鎖問題
所以苹丸,對于
dispatch_sync(queue, ^{});
這行代碼的意義可以概括為: 會阻塞當(dāng)前線程等待串行queue中的所有任務(wù)執(zhí)行完成后再向下執(zhí)行。
為什么NSString苇经、NSDictionary谈跛、NSArray要使用copy修飾符呢?
NSString塑陵、NSArray感憾、NSDictionary等經(jīng)常使用copy關(guān)鍵字,是因為他們有對應(yīng)的可變類型:NSMutableString令花、NSMutableArray阻桅、NSMutableDictionary 它們之間可能進行賦值操作,為確保對象中的字符串值不會無意間變動兼都,應(yīng)該在設(shè)置新屬性時拷貝一份嫂沉。
總結(jié):對于非集合類對象的copy操作如下:
[immutableObject copy]; //淺復(fù)制
[immutableObject mutableCopy]; //深復(fù)制
[mutableObject copy]; //深復(fù)制
[mutableObject mutableCopy]; //深復(fù)制采用同樣的方法可以驗證集合類對象的copy操作如下:
[immutableObject copy]; //淺復(fù)制
[immutableObject mutableCopy]; //單層深復(fù)制
[mutableObject copy]; //深復(fù)制
[mutableObject mutableCopy]; //深復(fù)制
*這個寫法會出什么問題: @property(copy)NSMutableArray array;
因為 copy 策略拷貝出來的是一個不可變對象,然而卻把它當(dāng)成可變對象使用,很容易造成程序奔潰這里還有一個問題,該屬性使用了同步鎖,會在創(chuàng)建時生成一些額外的代碼用于幫助編寫多線程程序,這會帶來性能問題,通過聲明 nonatomic 可以節(jié)省這些雖然
很小但是不必要額外開銷,在 iOS 開發(fā)中應(yīng)該使用 nonatomic 替代 atomic.
objc中向一個nil對象發(fā)送消息將會發(fā)生什么民鼓?
在 Objective-C 中向 nil 發(fā)送消息是完全有效的——只是在運行時不會有任何作用:
SomeClass * someObject;
someObject = nil;
[someObject doSomething];
objc在向一個對象發(fā)送消息時庙曙,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運行玫芦,然后在發(fā)送消息的時候慎王,objc_msgSend方法不會返回值蚓土,所謂的返回內(nèi)容都是具體調(diào)用時執(zhí)行的。那么赖淤,回到本題蜀漆,如果向一個nil對象發(fā)送消息,首先在尋找對象的isa指針時就是0地址返回了咱旱,所以不會出現(xiàn)任何錯誤确丢。
比較讓你混淆的是绷耍,僵尸對象。僵尸對象并不是nil鲜侥,僵尸對象是你的object被銷毀或者用于其他地方了褂始,但是指向它的指針還在。會發(fā)生向一個object發(fā)送一個它沒有的方法描函。
__block和__weak修飾符的區(qū)別
因此病袄,__block和__weak修飾符的區(qū)別其實是挺明顯的:
1.__block不管是ARC還是MRC模式下都可以使用,可以修飾對象赘阀,還可以修飾基本數(shù)據(jù)類型益缠。
2.__weak只能在ARC模式下使用,也只能修飾對象(NSString)基公,不能修飾基本數(shù)據(jù)類型(int)幅慌。
3.__block對象可以在block中被重新賦值,__weak不可以轰豆。
4.__block對象在ARC下可能會導(dǎo)致循環(huán)引用胰伍,非ARC下會避免循環(huán)引用,__weak只在ARC下使用酸休,可以避免循環(huán)引用骂租。
更多關(guān)于__block變量的詳細(xì)解釋,參見我的另一篇文章斑司,詳細(xì)探討了__block的實現(xiàn)原理
PS:__unsafe_unretained修飾符可以被視為iOS SDK 4.3以前版本的__weak的替代品渗饮,不過不會被自動置空為nil。所以盡可能不要使用這個修飾符宿刮。
__block
不用 __block, Block會捕獲棧上的變量(或指針)互站,將其復(fù)制為自己私有的const(變量), 捕獲他們的瞬時值。如果一個 block引用了一個棧變量或指針僵缺,那么這個block初始化的時候會擁有這個變量或指針的const副本胡桃,所以(被捕獲之后再在棧中改變這個變量或指針的值)是不起作用的);
__block修飾符所起到的作用就是只要觀察到該變量被block所持有磕潮,就將該變量在棧中的內(nèi)存地址放到堆中翠胰。
new和alloc/init的區(qū)別
概括來說,new和alloc/init在功能上幾乎是一致的自脯,分配內(nèi)存并完成初始化之景。
差別在于,采用new的方式只能采用默認(rèn)的init方法完成初始化冤今,
采用alloc的方式可以用其他定制的初始化方法闺兢。
MyClass *myObj = [MyClass alloc];
就是說alloc分配了一坨 內(nèi)存給對象屋谭,讓它不釋放,并且把地址返回給指針龟糕。那么這樣過后myobj為什么不能被使用呢桐磁?這是因為這片內(nèi)存還沒有被正確的初始化。
先看看 alloc 的API描述解說
- (id)alloc
Returns a new instance of the receiving class.
返回這個接受消息的類的一個實例.
The isa instance variable of the new instance is initialized to a data structure that describes the class; memory for all other instance variables is set to 0.
這個實例初始化后可以用來表示這個類的數(shù)據(jù)相關(guān)的結(jié)構(gòu);所有其他的實例變量的值都被設(shè)置成 0.
You must use an init... method to complete the initialization process. For example:
你必須使用 init... 方法來最終完成這個初始化的步驟,如下:
TheClass *newObject = [[TheClass alloc] init];
Do not override alloc to include initialization code. Instead, implement class-specific versions of init... methods.
不要重寫 alloc 來包含初始化的代碼.你可以使用指定版本的 init... 方法來達(dá)到你的目的.
For historical reasons, alloc invokes allocWithZone:.
由于歷史原因,allc 方法調(diào)用了 allocWithZone: 方法.
結(jié)論:
- alloc 后只是在系統(tǒng)中分配了內(nèi)存,這段內(nèi)存空間的大小與這個類本身結(jié)構(gòu)所占字節(jié)的大小相等,并返回了這段內(nèi)存的指針.
- alloc 將申請內(nèi)存空間中的值都設(shè)置為 0.
- alloc 調(diào)用了方法 allocWithZone:.
- alloc 就執(zhí)行了一次,沒有繼承的關(guān)系.
結(jié)論:
- 重寫 init 方法時需要先初始化父類的 init 方法.
- NSObject 中的 init 方法什么也沒做,只是返回了自己而已.
- 如果初始化失敗,會返回 nil.
<font color=#FF7F50 size=4>我的一些看法</font>
- OC僅僅支持單繼承,所以 init 方法是從繼承樹的頂部 NSObject 開始執(zhí)行的,每個鏈路中的對象都會執(zhí)行一次 init 操作,所以, init 操作會執(zhí)行多次,至上而下.
- alloc 就只執(zhí)行一次.
- 這些雖然看似簡單基礎(chǔ),但對于如何去中等程度的封裝一個類是很有用的,因為有時候,你覺得你懂了,可是你在封裝一個面向?qū)ο蟮囊粋€類的時候,莫名其妙的崩潰,你就傻眼了,因為我遇到過才會寫出來.
- 封裝分為多種,初級封裝以及中等程度的封裝,初級封裝會暴露出被你封裝的相關(guān)類的信息,比如返回值,入?yún)⑹裁吹?屬于初級階段.
_block和__weak的區(qū)別
__weak 和 __block 關(guān)鍵字的區(qū)別
__weak與__block區(qū)別讲岁,深層理解兩者區(qū)別
1我擂,在MRC 時代,__block 修飾可以避免循環(huán)引用缓艳;ARC時代校摩,__block 修飾,同樣會引起循環(huán)引用問題阶淘;
2衙吩,__block不管是ARC還是MRC模式下都可以使用,可以修飾對象溪窒,還可以修飾基本數(shù)據(jù)類型坤塞;
3,__weak只能在ARC模式下使用澈蚌,也只能修飾對象摹芙,不能修飾基本數(shù)據(jù)類型;
4宛瞄,__block對象可以在block中被重新賦值浮禾,__weak不可以;
5份汗,__unsafe_unretained修飾符可以被視為iOS SDK 4.3以前版本的__weak的替代品伐厌,不過不會被自動置空為nil。所以盡可能不要使用這個修飾符裸影。(__weak 會自動置為nil)
對象回收時Weak指針自動被置為nil的實現(xiàn)原理
__weak如何實現(xiàn)對象值自動設(shè)置為nil的
copy和mutableCopy