更多內(nèi)容請挪步我的博客
前言
最近择浊,遇到一個崩潰問題窍育,崩潰前控制臺打印出 "message send to dealloc instance",在解決問題的過程中發(fā)現(xiàn)一個調(diào)試工具。
話說,這一次的問題主要是發(fā)生在 MRC 環(huán)境下工闺,除了發(fā)現(xiàn)調(diào)試工具,還回憶了一下 MRC瓣蛀。感興趣的話陆蟆,可以當作面試題來看看下面代碼有什么問題。
上一次用到 MRC 恐怕還是 2011 年惋增,這次是因為公司有個老代碼使用的是很早以前別人寫的庫叠殷,一直沒有升級所以還是在 MRC 下。先來描述下問題诈皿。
崩潰代碼
重要的事情說三遍林束,MRC 下,MRC 下稽亏,MRC 下
//.h 頭文件中定義了一個變量
NSString *_title;
//.m 實現(xiàn)文件中
- (id)initWithTitle:(NSString *)title {
_title = [title copy];
// …
}
- (void)dealloc {
[_title release];
[super dealloc];
}
// 另一個調(diào)用文件中是這樣子的
[[xxClass alloc] initWithTitle:nil];
最近的一次修改導致崩潰是被改成了下面的樣子壶冒,當 title 為 nil 時把它定義為默認字符串,于是上面的 initWithTitle 改為
- (id)initWithTitle:(NSString *)title {
if (title) {
_title = [title copy];
} else {
_title = NSLocalizedString(@"提示", nil);
}
}
接下來的事情是這樣的截歉,某個頁面在多次調(diào)用 initWithTitle 的時候會發(fā)生崩潰依痊,錯誤信息是這樣子的:
UncaughtExceptionHandlerAddressesKey = (
4 CoreFoundation 0x1b9ca81c <redacted> + 2124,
5 CoreFoundation 0x1b9c9f8f CFDictionaryGetValue + 126,
6 CoreFoundation 0x1bb1446b <redacted> + 142,
7 CoreFoundation 0x1bb14111 CFBundleCopyLocalizedStringForLocalization + 94,
8 CoreFoundation 0x1ba0f6bb CFBundleCopyLocalizedString + 18,
調(diào)試的話,崩潰的位置就在 _title = NSLocalizedString(@"提示", nil); 這一行怎披。
不知道各位看官是否一眼發(fā)現(xiàn)了問題,控制臺還有這樣的輸出
message sent to deallocated instance 0x6d564f0
一時沒想到 NSLocalizedString 會有什么問題瓶摆,因為好久不用 MRC凉逛,武功已經(jīng)全廢,所以關(guān)注上了這個 message sent to deallocated instance群井,想通過這條信息查找到問題状飞。
malloc_history 的使用
網(wǎng)上一通搜索,發(fā)現(xiàn)果然能查到 0x6d564f0 這個地址對應(yīng)的代碼书斜,只要在 Xcode -> Scheme -> Arguments 設(shè)置中添加環(huán)境變量 MallocStackLogging 為 YES诬辈,在終端輸入下列命令即可
malloc_history 32009 0x6d564f0
其中 32009 是 pid,pid 在 Xcode 的控制臺會打蛹黾(這個方法只能檢測模擬器上的問題)焙糟,如下
2016-11-17 16:19:57:837 MyProject[32009:304745]
命令執(zhí)行后輸出,終端輸出
Invalid connection: com.apple.coresymbolicationd
malloc_history Report Version: 2.0
ALLOC 0x7b786d90-0x7b786d9f [size=16]: thread_92ec000 | start | main | UIApplicationMain | -[UIApplication _run] | CFRunLoopRunInMode | CFRunLoopRunSpecific | _ CFRunLoopDoObservers | _ CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ | CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) | CA::Transaction::commit() | CA::Context::commit_transaction(CA::Transaction*) | CA::Layer::layout_and_display_if_needed(CA::Transaction*) | CA::Layer::layout_if_needed(CA::Transaction*) | -[CALayer layoutSublayers] | -[NSObject performSelector:withObject:] | -[UIView(CALayerDelegate) layoutSublayersOfLayer:] | -[UILayoutContainerView layoutSubviews] | -[UINavigationController __viewWillLayoutSubviews] | -[UINavigationController _startDeferredTransitionIfNeeded:] | -[UINavigationController _startTransition:fromViewController:toViewController:] | -[UINavigationController _updateScrollViewFromViewController:toViewController:] | -[UINavigationController _layoutViewController:] | -[UIViewController loadViewIfRequired] | -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] | -[WSLoginViewController viewDidLoad] | -[WSLoginViewController createLoginBoxView] | -[WSLoginViewController createTextField] | -[NSBundle localizedStringForKey:value:table:] | CFBundleCopyLocalizedString | CFBundleCopyLocalizedStringForLocalization | _copyStringFromTable | CFPropertyListCreateWithData | _CFPropertyListCreateWithData | __CFTryParseBinaryPlist | __CFBinaryPlistCreateObjectFiltered | __CFBinaryPlistCreateObjectFiltered | CFStringCreateWithCharacters | __CFStringCreateImmutableFunnel3 | _CFRuntimeCreateInstance | CFAllocatorAllocate | __CFAllocatorSystemAllocate
哇样屠,這么長穿撮,怎么看缺脉,其實只要關(guān)注最后面的幾句
[WSLoginViewController createTextField]
| -[NSBundle localizedStringForKey:value:table:] |
CFBundleCopyLocalizedString
一般情況下問題就是最后的代碼導致問題。
揭秘崩潰原因
繞了一圈其實還是回到原始悦穿,就看 _title = NSLocalizedString(@"提示", nil); 如果已經(jīng)打開僵尸調(diào)試一般都是能斷點進入到相應(yīng)代碼攻礼。萬一沒開,試試 malloc_histroy 還不錯栗柒〗赴纾看久一會突然就想到了,直接賦值靜態(tài)字符串是不需要釋放的瞬沦,dealloc 的時候會有 release 調(diào)用太伊,所以改成了如下方式
_title = [[NSString alloc] initWithString:NSLocalizedString(@"提示", nil)];
總結(jié)
雖然平時已經(jīng)好久不用 MRC 了,但是發(fā)現(xiàn)個 malloc_history 也還不錯蛙埂。
參考內(nèi)容
message sent to deallocated instance問題的解決方法
內(nèi)存管理的簡單總結(jié)
Xcode 調(diào)試