Objective-C EXC_BAD_ACCESS

來源:互聯網
上載者:User

寫程式遇到 Bug 並不可怕,大部分的問題,通過簡單的 Log 或者
程式碼分析並不難找到原因所在。但是在 Objective-C 編程中遇到 EXC_BAD_ACCESS
問題的時候,通過簡單常規的手段很難發現問題。這篇文章,給大家介紹一個常用的尋找 EXC_BAD_ACCESS 問題根源的方法。

首先說一下 EXC_BAD_ACCESS 這個錯誤,可以這麼說,90%的錯誤來源在於對一個已經釋放的對象進行release操作。

舉一個簡單的例子來說明吧,首先看一段Java代碼:

public class Test{
        public static
void
main(String[] args){
                String s = “This is a test string”
;
                s = s.substring(s.indexOf(“a”
),(s.length
()));
                System.out
.println(s);
                
        }
}

這種寫法在Java中很常見也很普遍,這不會產生任何問題。但是到了 Objective-C 中,就會出事,考慮這個程式:

#import
<Foundation/Foundation.h>

int
main (int
argc, const
char
* argv[]) {
NSAutoreleasePool
* pool = [[NSAutoreleasePool
alloc
] init
];
        NSString
* s = [[NSString
alloc
]initWithString
:@”This is a test string”
];
        s = [s substringFromIndex
:[s rangeOfString
:@"a"
].location
];//記憶體泄露

        [s release
];//錯誤釋放

[pool drain
];//EXC_BAD_ACCESS

return
0
;
}

這個例子當然狠容易的看出問題所在,如果這段程式碼封裝含在一個很大的邏輯中,確實容易被忽略。Objective-C 這段代碼有三個致命問題,1,記憶體泄露。2,錯誤釋放。3,造成 EXC_BAD_ACCESS 錯誤。

1, NSString
* s = [[NSString
alloc
]initWithString
:@”This is a test string”
]; 建立了一個 NSString Object,隨後的 s = [s substringFromIndex
:[s rangeOfString
:@"a"
].location
]; 執行後,導致建立的對象引用消失,直接造成記憶體泄露。

2,錯誤釋放。[s release
];
這個問題,原因之一是一個邏輯錯誤,以為 s 還是我們最初建立的那個 NSString 對象。第二是因為從
substringFromIndex:(NSUInteger i) 這個方法返回的 NSString 對象,並不需要我們來釋放,它其實是一個被
substringFromIndex 方法標記為 autorelease 的對象。如果我們強行的釋放了它,那麼會造成
EXC_BAD_ACCESS 問題。

3: EXC_BAD_ACCESS。由於 s 指向的 NSString 對象被標記為 autorelease, 則在
NSAutoreleasePool 中已有記錄。但是由於我們在前面錯誤的釋放了該對象,則當 [pool drain]
的時候,NSAutoreleasePool 又一次的對它記錄的 s 對象調用了 release 方法,但這個時候 s
已經被釋放不複存在,則直接導致了 EXC_BAD_ACCESS問題。

那麼,知道了 EXC_BAD_ACCESS 的誘因之一後,如何快速高效的定位問題?

1: 為工程運行時加入 NSZombieEnabled 環境變數,並設為啟用,則在 EXC_BAD_ACCESS 發生時,XCode 的 Console 會列印出問題描述。

首先雙擊 XCode 工程中,Executables 下的 可執行模組,

在快顯視窗中,Variables to be set in the environment,添加 NSZombieEnabled,並設定為 YES,點擊選中複選框啟用此變數。

這樣,運行上述 Objective-C 時會看到控制台輸出:

Untitled[3646:a0f] *** -[CFString release]: message sent to deallocated instance 0x10010d340

這條訊息對於定位問題有很好的提示作用。但是很多時候,只有這條提示是不夠的,我們需要更多的提示來協助定位問題,這時候再加入 MallocStackLogging 來啟用malloc記錄。



當錯誤發生後,在終端執行:

malloc_history ${App_PID} ${Object_instance_addr} 則會獲得相應的 malloc 記錄,比如對於上一個控制台輸出

Untitled[3646:a0f] *** -[CFString release]: message sent to deallocated instance 0x10010d340


則我們可以在終端執行

結果如下:

Buick-Wongs-MacBook-Pro:Downloads buick$ malloc_history 3646 0x10010d340
malloc_history Report Version: 2.0
Process: Untitled [3646]
Path: /Users/buick/Desktop/Untitled/build/Debug/Untitled
Load Address: 0×100000000
Identifier: Untitled
Version: ??? (???)
Code Type: X86-64 (Native)
Parent Process: gdb-i386-apple-darwin [3638]

Date/Time: 2011-02-01 15:07:04.181 +0800
OS Version: Mac OS X 10.6.6 (10J567)
Report Version: 6

ALLOC 0x10010d340-0x10010d357 [size=24]: thread_7fff70118ca0 |start |
main | objc_msgSend | lookUpMethod | prepareForMethodLookup |
_class_initialize | +[NSString initialize] | objc_msgSend | lookUpMethod
| prepareForMethodLookup | _class_initialize | NXCreateMapTableFromZone
| malloc_zone_malloc
—-
FREE 0x10010d340-0x10010d357 [size=24]: thread_7fff70118ca0 |start |
main | objc_msgSend | lookUpMethod | prepareForMethodLookup |
_class_initialize | _finishInitializing | free

ALLOC 0x10010d340-0x10010d357 [size=24]: thread_7fff70118ca0 |start |
main | -[NSPlaceholderString initWithString:] | objc_msgSend |
lookUpMethod | prepareForMethodLookup | _class_initialize |
_class_initialize | +[NSMutableString initialize] | objc_msgSend |
lookUpMethod | prepareForMethodLookup | _class_initialize |
NXCreateMapTableFromZone | malloc_zone_malloc
—-
FREE 0x10010d340-0x10010d357 [size=24]: thread_7fff70118ca0 |start |
main | -[NSPlaceholderString initWithString:] | objc_msgSend |
lookUpMethod | prepareForMethodLookup | _class_initialize |
_class_initialize | _finishInitializing | free

ALLOC 0x10010d340-0x10010d35f [size=32]: thread_7fff70118ca0 |start |
main | -[NSCFString substringWithRange:] | CFStringCreateWithSubstring |
__CFStringCreateImmutableFunnel3 | _CFRuntimeCreateInstance |
malloc_zone_malloc

這樣就可以很快的定位出問題的程式碼片段了,注意輸出的最後一行,,,這行雖然不是問題的最終原因,但是離問題點已經很近了,隨著它找下去,八成就會找到問題。

當然,EXC_BAD_ACCESS 的定位方法還有很多,隨著具體問題的不同而不同。歡迎各位討論。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.