此篇我將羅列出iOS開(kāi)發(fā)中,Xcode編譯器出現(xiàn)的常見(jiàn)錯(cuò)誤,警告視同為錯(cuò)誤處理健爬。(序號(hào)只做排序用)
1.死存儲(chǔ)問(wèn)題
這個(gè)問(wèn)題上篇文章提到過(guò)埋泵,之所以重新羅列出來(lái)幔欧,是因?yàn)檫@個(gè)問(wèn)題也挺常見(jiàn)的。導(dǎo)致這個(gè)問(wèn)題的原因是丽声,當(dāng)我們對(duì)某個(gè)對(duì)象直接賦值之后礁蔗,如果包含這個(gè)對(duì)象的方法立馬就結(jié)束了,那么這個(gè)對(duì)象就會(huì)被自動(dòng)釋放雁社,但是我們本身根本還沒(méi)有用到它浴井,所以就形成了一個(gè)死存儲(chǔ)代碼,這個(gè)賦值語(yǔ)句其實(shí)也是無(wú)效的歧胁。
2.訪問(wèn)了某個(gè)不存在的方法
ObjC 的方法調(diào)用跟 C++ 很不一樣滋饲。 C++ 在編譯的時(shí)候就已經(jīng)綁定了類和方法,一個(gè)類不可能調(diào)用一個(gè)不存在的方法喊巍,否則就報(bào)編譯錯(cuò)誤屠缭。而 ObjC 則是在 runtime 的時(shí)候才去查找應(yīng)該調(diào)用哪一個(gè)方法。
這兩種實(shí)現(xiàn)各有優(yōu)劣崭参,C++ 的綁定使得調(diào)用方法的時(shí)候速度很快呵曹,但是只能通過(guò) virtual 關(guān)鍵字來(lái)實(shí)現(xiàn)有限的動(dòng)態(tài)綁定。而對(duì) Objective-C語(yǔ)言來(lái)說(shuō)何暮,本質(zhì)上它使用的是“消息結(jié)構(gòu)”而非“函數(shù)調(diào)用”奄喂,因?yàn)镺bjC是由Smalltalk演化而來(lái)的,后者是消息型語(yǔ)言的鼻祖海洼。
關(guān)鍵區(qū)別在于:使用消息結(jié)構(gòu)的語(yǔ)言跨新,其運(yùn)行時(shí)所應(yīng)執(zhí)行的代碼由運(yùn)行環(huán)境來(lái)決定;而使用函數(shù)調(diào)用的語(yǔ)言坏逢,則由編譯器決定域帐。如果代碼中調(diào)用的函數(shù)是多態(tài)的,那么采用函數(shù)調(diào)用方式的語(yǔ)言就要按照“虛方法表(virual table)”來(lái)查出到底應(yīng)該執(zhí)行哪個(gè)函數(shù)來(lái)實(shí)現(xiàn)是整。
采用消息結(jié)構(gòu)的語(yǔ)言肖揣,不論是否多態(tài),總是在運(yùn)行時(shí)才會(huì)查找所要執(zhí)行的方法浮入。實(shí)際上龙优,編譯器甚至不關(guān)心接受消息的對(duì)象是何種類型。接收消息的對(duì)象問(wèn)題也要在運(yùn)行時(shí)處理事秀,其過(guò)程就是“動(dòng)態(tài)綁定”彤断。
舉個(gè)例子野舶,比如下面這行代碼
[self newMethod];
假設(shè)我們調(diào)用了一個(gè)不存在的方法,就會(huì)報(bào)出unrecognized selector sent to instance的異常瓦糟。其實(shí)我們用respondsToSelector:方法判斷一下即可筒愚。大多數(shù)情況下,可能在使用delegate的時(shí)候菩浙,需要加一下判斷巢掺,因?yàn)閐elegate通常是一個(gè)id類型(泛類型)。
if ([self respondsToSelector:@selector(newMethod)]) { [self newMethod]; }
3.堆棧溢出
多數(shù)情況下應(yīng)用程序是不需要考慮堆和棧的大小的劲蜻,總是當(dāng)作足夠大來(lái)使用就能滿足一般業(yè)務(wù)開(kāi)發(fā)陆淀。但是事實(shí)上堆和棧都不是無(wú)上限的,過(guò)多的遞歸會(huì)導(dǎo)致棧溢出先嬉,過(guò)多的 alloc 變量會(huì)導(dǎo)致堆溢出轧苫。
iOS內(nèi)存布局原理如下圖所示:
一個(gè)運(yùn)行時(shí)進(jìn)程的典型分布具體有五個(gè)部分,分別是代碼段疫蔓,初始化數(shù)據(jù)段含懊,未初始化數(shù)據(jù)段,堆衅胀,棧岔乔。
在應(yīng)用程序分配的內(nèi)存空間里面,最低地址位是固定的代碼段和數(shù)據(jù)段滚躯,往上是堆雏门,用來(lái)存放全局變量,對(duì)于 ObjC 來(lái)說(shuō)掸掏,就是 alloc 出來(lái)的變量茁影,都會(huì)放進(jìn)這里,堆不夠用的時(shí)候就會(huì)往上申請(qǐng)空間丧凤。最頂部高地址位是棧募闲,局部的基本類型變量都會(huì)放進(jìn)棧里。 ObjC 的對(duì)象都是以指針進(jìn)行操控的愿待,局部變量的指針都在棧里浩螺,全局的變量在堆里,而無(wú)論是什么指針呼盆,alloc 出來(lái)的都在堆里年扩,所以 alloc 出來(lái)的變量一定要記得 release蚁廓。
對(duì)于 autorelease 變量來(lái)說(shuō)访圃,每個(gè)函數(shù)有一個(gè)對(duì)應(yīng)的 autorelease pool,函數(shù)出棧的時(shí)候 pool 被銷(xiāo)毀相嵌,同時(shí)調(diào)用這個(gè) pool 里面變量的 dealloc 函數(shù)來(lái)實(shí)現(xiàn)其內(nèi)部 alloc 出來(lái)的變量的釋放腿时。
下面的鏈接能夠更好的幫助你理解堆棧况脆,不過(guò)是在stackoverflow
上的,全英文批糟,你可以逐句翻譯格了,有助你理解。
http://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap
4.多線程并發(fā)操作
這個(gè)問(wèn)題應(yīng)該是日常開(kāi)發(fā)操作中都會(huì)遇到的問(wèn)題了徽鼎。當(dāng)某個(gè)對(duì)象會(huì)被多個(gè)線程修改的時(shí)候盛末,有可能一個(gè)線程訪問(wèn)這個(gè)對(duì)象的時(shí)候另一個(gè)線程已經(jīng)把它刪掉了,導(dǎo)致 Crash否淤。比較常見(jiàn)的是在網(wǎng)絡(luò)任務(wù)隊(duì)列里面悄但,主線程往隊(duì)列里面加入任務(wù),網(wǎng)絡(luò)線程同時(shí)進(jìn)行刪除操作導(dǎo)致掛掉石抡。
這個(gè)問(wèn)題的解決方案就是對(duì)某個(gè)線程加上線程鎖檐嚣,舉個(gè)例子:
- (void)addStudent:(Student *)student {
if (student != nil) {
NSLock* aLock = [[NSLock alloc] init];//創(chuàng)建線程鎖
[aLock lock];//加鎖
[students addObject:student];
[aLock unlock];//解鎖
}else{
return;
}}
也可以使用關(guān)鍵字synchronized來(lái)簡(jiǎn)化代碼
- (void)addStudent:(Student *)student {
if (student == nil) return;
@synchronized(players) {
[students addObject:student];
}
}
synchronized關(guān)鍵字其實(shí)就是互斥鎖,意思是當(dāng)上一個(gè)線程的任務(wù)沒(méi)有執(zhí)行完畢的時(shí)候(被鎖讍浮)嚎京,那么下一個(gè)線程會(huì)進(jìn)入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢,當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢隐解,下一個(gè)線程會(huì)自動(dòng)喚醒然后執(zhí)行任務(wù)鞍帝。
在日常開(kāi)發(fā)中,盡量不要加鎖厢漩,因?yàn)槎嗑€程開(kāi)發(fā)本身就是為了提高程序執(zhí)行順序膜眠,而同步鎖本身就只能一個(gè)進(jìn)程執(zhí)行,這樣不免降低執(zhí)行效率溜嗜,除非必須加鎖宵膨,哪里有隱患,就在那里加鎖炸宵,在使用互斥鎖的時(shí)候辟躏,要盡可能縮小它的范圍,范圍越大土全,效率越差捎琐。
當(dāng)然,線程鎖很分很多種裹匙,上面代碼片段中用到的只是普通的線程鎖瑞凑。iOS中的線程鎖,還有以下幾種:
1)NSRecursiveLock 遞歸鎖:使用普通的 NSLock 如果在遞歸的情況下或者重復(fù)加鎖的情況下概页,自己跟自己搶資源導(dǎo)致死鎖籽御。Cocoa 提供了 NSRecursiveLock 鎖可以多次加鎖而不會(huì)死鎖,只要 unlock 次數(shù)跟 lock 次數(shù)一樣就行了。
2)NSConditionLock 條件鎖:多數(shù)情況下鎖是不需要關(guān)心什么條件下 unlock 的技掏,要用的時(shí)候鎖上铃将,用完了就 unlock 就完了。Cocoa 提供這種條件鎖哑梳,可以在滿足某種條件下才解鎖劲阎。這個(gè)鎖的 lock 和 unlock, lockWhenCondition 是隨意組合的,可以不用對(duì)應(yīng)起來(lái)鸠真。
3)pthread_mutex_t:同步鎖悯仙,基于C語(yǔ)言的同步鎖機(jī)制,使用方法與其他同步鎖機(jī)制類似吠卷。
4)NSDistributedLock 分布式鎖:這是用在多進(jìn)程之間共享資源的鎖雁比,現(xiàn)在不涉及
更多的有關(guān)線程鎖的內(nèi)容,請(qǐng)閱讀下面的博客撤嫩,很有收獲
李劍飛的技術(shù)博客
下面是一些具體的錯(cuò)誤處理
1.warning: Could not resolve external type c:objc(cs)NSObject
這個(gè)問(wèn)題我最近遇到過(guò)偎捎,之前個(gè)人寫(xiě)的一個(gè)小項(xiàng)目(Xcode6.2時(shí)候?qū)懙模驗(yàn)閄code7版本更新的緣故序攘,導(dǎo)致諸如下圖2-1所示的小問(wèn)題茴她,后來(lái)自己更改之后,又在原項(xiàng)目中集成了BugTags的SDK程奠,然后編譯未通過(guò)丈牢,出現(xiàn)了上述錯(cuò)誤,當(dāng)時(shí)忘了截圖瞄沙。
我在Stack Overflow找到了調(diào)試方法己沛,步驟如下:
- Go to Build Settings -> Build Options -> Debug Information Format
- Change the Debug setting from "DWARF with dSYM File" to "DWARF"
- Leave the Release setting at "DWARF with dSYM File"
The problem appears to be that Xcode was trying to create dSYM files for Debug builds. You don't need dSYM files for Debug builds -- it's release builds where you need them.
什么是dSYM文件?
Xcode編譯項(xiàng)目后距境,我們會(huì)看到一個(gè)同名的 dSYM 文件申尼,dSYM 是保存 16 進(jìn)制函數(shù)地址映射信息的中轉(zhuǎn)文件,我們調(diào)試的 symbols 都會(huì)包含在這個(gè)文件中垫桂,并且每次編譯項(xiàng)目的時(shí)候都會(huì)生成一個(gè)新的 dSYM 文件师幕,位于 /Users/<用戶名>/Library/Developer/Xcode/Archives 目錄下,對(duì)于每一個(gè)發(fā)布版本我們都很有必要保存對(duì)應(yīng)的 Archives 文件.
更多有關(guān)dSYM的內(nèi)容請(qǐng)點(diǎn)擊如下鏈接
dSYM文件分析工具
2. -[__NSArrayI objectAtIndex:]: index 100 beyond bounds [0 .. 1]'
問(wèn)題屬于數(shù)組越界的問(wèn)題诬滩,下拉刷新霹粥,上拉加載tableview的時(shí)候容易出現(xiàn)。這個(gè)問(wèn)題需要放到與你相對(duì)應(yīng)的工作環(huán)境中解決疼鸟,一些比如數(shù)組是從零開(kāi)始取下標(biāo)的細(xì)節(jié)后控,都是可能疏忽出錯(cuò)的地方。
請(qǐng)參考下面的博客
取自CSDN博客
3.升級(jí)Xcode7后空镜,舊項(xiàng)目無(wú)法加載網(wǎng)絡(luò)數(shù)據(jù)
這個(gè)問(wèn)題浩淘,Xcode7剛更新的時(shí)候很常見(jiàn)矾利,這是因?yàn)閕OS9引入了新特性 APP Transport Security (ATS:簡(jiǎn)單理解意思是:應(yīng)用傳輸安全...翻譯可能不太準(zhǔn)確, 只是字面意思的理解, 歡迎指正)
由于新特性要求APP內(nèi)訪問(wèn)的網(wǎng)絡(luò)必須是使用HTTPS協(xié)議, 查詢到, 這個(gè)協(xié)議相對(duì)于HTTP協(xié)議較安全, 但是目前很多公司, 很多項(xiàng)目依舊是使用HTTP協(xié)議, 有時(shí)候也不能立馬改成HTTPS協(xié)議, 只能在工程中進(jìn)行修改.
修改步驟如下:
1)打開(kāi)項(xiàng)目中Info.plist文件
2)在plist文件中添加項(xiàng)NSAPPTransportSecurity,類型為字典馋袜。
3)添加子項(xiàng)NSAllowsArbitraryLoads,類型為Boolean舶斧,值為YES欣鳖。如圖4-1
4)Command+R即可
4.數(shù)組中存在空對(duì)象
這個(gè)問(wèn)題是因?yàn)槟硞€(gè)數(shù)組中插入的對(duì)象為空值,在出錯(cuò)的地方打斷點(diǎn)調(diào)試即可茴厉。在Debug模式下可能不會(huì)報(bào)錯(cuò)泽台,但是在release模式下會(huì)出錯(cuò)。因?yàn)镺bjective-C是一門(mén)動(dòng)態(tài)語(yǔ)言矾缓,數(shù)組對(duì)象為空的問(wèn)題在運(yùn)行期環(huán)境可能不會(huì)出錯(cuò)怀酷,但是放在工作環(huán)境中就會(huì)因?yàn)閷?duì)象為nil報(bào)錯(cuò)。
其實(shí)這個(gè)問(wèn)題完全可以在代碼編寫(xiě)的時(shí)候優(yōu)化嗜闻,那就是在創(chuàng)建數(shù)組的時(shí)候多使用字面量語(yǔ)法蜕依,也就是語(yǔ)法糖的形式×瘀ǎ或者對(duì)可能出現(xiàn)對(duì)象為空值的數(shù)組加if語(yǔ)句判斷样眠。
請(qǐng)參考以下鏈接http://www.2cto.com/kf/201501/369307.html
神書(shū)《Effective Objective-C 2.0》對(duì)這個(gè)問(wèn)題有深層次的解釋,可以去看看翠肘,看過(guò)的人都說(shuō)好檐束。
5.[BEROR]CodeSign error: Certificate identity ‘iPhone Distribution: ***.’ appears more than once in the keychain. The codesign tool requires there only be one.
上述錯(cuò)誤是在項(xiàng)目進(jìn)行Archive打包時(shí)出現(xiàn)的。
原因:出現(xiàn)此問(wèn)題的原因是多個(gè)證書(shū)之間沖突造成束倍。
解決方案:打開(kāi)mac系統(tǒng)的“實(shí)用工具”-“鑰匙串訪問(wèn)”-“我的證書(shū)”中被丧,如果看到有重復(fù)的證書(shū)名,那么請(qǐng)將早期的證書(shū)刪除掉绪妹,重啟Xcode甥桂。
6.Could not change executable permissions on the application.
原因:擁有相同的bundle Identifier已經(jīng)在設(shè)備上運(yùn)行。
解決辦法:刪除設(shè)備中或者模擬器中的App邮旷。
7.ld: library not found for -lmp3lameclang: error: linker command failed with exit code 1 (use -v to see invocation).
原因:在工作中格嘁,一般是多人編輯同一個(gè)工程時(shí)其中一人沒(méi)將某個(gè)庫(kù)上傳導(dǎo)致的。
解決方案:上傳具體靜態(tài)庫(kù)
8.A valid provisioning profile matching the application's Identifier 'XXXX' could not be found
原因:當(dāng)編譯后出現(xiàn)上述問(wèn)題廊移,缺少證書(shū)或者是在Code Signing Identity處沒(méi)有選擇對(duì)應(yīng)的證書(shū)或者是證書(shū)不對(duì)應(yīng)糕簿。
解決辦法:重裝證書(shū),檢查證書(shū)是否是否選擇是否對(duì)應(yīng)狡孔。
9.在ARC項(xiàng)目中懂诗,使用了MRC模式開(kāi)發(fā)的第三方庫(kù)
1)打開(kāi)項(xiàng)目所屬的TARGETS設(shè)置
2)點(diǎn)擊Build Phases選項(xiàng),進(jìn)入到子項(xiàng)Compile Sources中苗膝,找到使用MRC的文件殃恒,添加-fno-objc-arc標(biāo)記,表明在編譯時(shí),該文件使用MRC編譯
3)相反的离唐,如果想在MRC項(xiàng)目中病附,使用ARC的文件,可以使用 -fobjc-arc 標(biāo)記即可
總結(jié):
今天先羅列出這么多亥鬓,下篇我將繼續(xù)總結(jié)日常開(kāi)發(fā)中完沪,Xcode拋出的各種異常和錯(cuò)誤,供大家參考嵌戈,如有不對(duì)之處覆积,請(qǐng)您指正,謝謝熟呛!
更新
1.Xcode編譯出現(xiàn)Link錯(cuò)誤宽档,出現(xiàn)“duplicate symbols for architecture i386 clang”提示
問(wèn)題:鏈接時(shí),項(xiàng)目有重名文件
解決方案:
1.在Xcode的Targets->Bulid Phases-Link Binary With Libraries中查看有無(wú)重復(fù)的lib
2.全工程搜索下重名文件
2.關(guān)于category位于靜態(tài)庫(kù)庵朝,引用該靜態(tài)庫(kù)的工程使用其中的Category吗冤,出現(xiàn)“unrecognized sent to class”提示。
問(wèn)題:標(biāo)準(zhǔn)靜態(tài)庫(kù)與OC之間的linker的差異九府。在標(biāo)準(zhǔn)的UNIX靜態(tài)庫(kù)欣孤,linker symbol是依照每一個(gè)類別而產(chǎn)生的,但由于category并沒(méi)有產(chǎn)生一個(gè)類別昔逗,所以拋出上述錯(cuò)誤降传。
解決辦法:
1.在該靜態(tài)庫(kù)的Targets->Bulid Setting->Linker flags->在其中添加-Objc
2.在使用該靜態(tài)庫(kù)的工程Targets->Build Setting->Linker Flags->加上-all_load或者-force_load
3.warning:ld:warning directory not found for option'-L'
問(wèn)題:通常是path問(wèn)題
解決:
TargetsText 里面找到Build Setting ,然后Library Search Paths和Framework Search Paths勾怒,刪除掉編譯報(bào)warning的路徑即可婆排。