用NSZombieEnabled解决恼人的EXC_BAD_ACCESS错误

更新Xcode 4设置NSZombieEnabled方法:
按住Option点Run按钮,在出现的窗口中选择Arguments,在Environment Variables里面添加NSZombieEnabled,Value下面添YES

以下为XCode 3的设置方法

这个方法也许很多人都知道了,但是毕竟有不知道的,比如在写这篇文章之前10分钟的我。先说明情况:昨天遇到了这样的问题,程序突然Crash,出现EXC_BAD_ACCESS错误,但是不是每次都Crash,有几次RP高了就好使,Debug时最终基本停在objc-msgsend这里,但也不是每次,是大部分(90%)。十分令人费解,搞了几个小时无果,求助google和stackoverflow终于解决。

首先解释下EXC_BAD_ACCESS,当你向已经释放的对象发送消息时就会出现这种错误。

至于NSZombieEnabled,就是当设置NSZombieEnabled环境变量后,一个对象销毁时会被转化为_NSZombie,个人感觉和线程的那几个状态有些相似,设置NSZombieEnabled后,当你向一个已经释放的对象发送消息,这个对象就不会向之前那样Crash或者产生一个难以理解的行为,而是放出一个错误消息,然后以一种可预测的可以产生debug断点的方式消失(原文是die),因此我们就可以找到具体或者大概是哪个对象被错误的释放了。

设置NSZombieEnabled的方法如下

  1. 在XCode左边那个Groups & Files栏中找到Executables,双击其中的一项,或者右键Get Info;
  2. 切换到Arguments
  3. 这里一共有两个框,在下面那个Variables to be set in the environment:点+号添加一项,Name里填NSZombieEnabled,Value填Yes,要保证前面的钩是选中的。

好了,赶紧去找哪里出了问题吧,至于取消NSZombieEnabled,就是吧刚才提到的那个钩取消即可。

最后总结一下感想,首先是我对EXC_BAD_ACCESS这个错误还没有足够的认识,其次是我没有尽快的去搜索一下解决方法,这两点导致我浪费了大量时间做各种各样奇怪的调试。幸好现在解决了,可以继续工作了。

2012.02.15更新:

在debug过程中,你可能发现启用NSZombieEnabled后,程序不再crash,而一旦去掉NSZombieEnabled,程序再次crash。此时NSZombieEnabled已经无法解决你的问题,只能遵照内存管理原则仔细查找问题出处,至于这种问题的产生原因,个人认为可能是NSZombieEnabled在一定程度上延长了一个object的生命周期,而延长的这段时间恰好突破了EXC_BAD_ACCESS的临界点,从而避免了EXC_BAD_ACCESS的发生。

–以上–

XCode技巧之UserScripts

XCode作为一款强大的IDE,当然也支持脚本功能。通过添加自己的脚本我们可以很方便的完成我们的工作。

在XCode中点击 Edit User Scripts即可方便的添加脚本,而且XCode自带的脚本也相当于给我们提供了丰富的样例。

比如我们添加一个这样的脚本,并为其添加一个快捷键⇧⌘P(快捷键添加方法参考Comments分类中的Un/Comment Selection脚本)

 

#!/bin/sh

echo "%%%{PBXSelection}%%%"
echo "#pragma mark -"
echo "#pragma mark %%%{PBXSelectedText}%%%"
echo "%%%{PBXSelection}%%%"

注意在Output下拉列表中选择Replace Selection。这个脚本的作用是方便我们建立#pragma,首先提供pragma的名字,然后选中这个名字,按相应快捷键,#pragma就自动完成了,很方便。

这个脚本也可以这样写,我们就不用每行都写echo了

 

#!/bin/sh

cat << EOF
%%%{PBXSelection}%%%
#pragma mark -
#pragma mark %%%{PBXSelectedText}%%%
%%%{PBXSelection}%%%
EOF

除了bash脚本,XCode还支持Ruby,Python等多种语言的脚本,比如这个Ruby脚本

 

#!/usr/bin/env ruby -w

# Source: http://allancraig.net/blog/?p=315

properties = ''
synthesize = ''
release    = ''

STDIN.read.each do |line|
	line.gsub!(/\*/, '').strip!
	words = line.split(/\s+/)
	
	label = words.size > 2 ? words[1] : words[0]
	variable = words[-1]
	properties << "@property (nonatomic, retain) IBOutlet #{label} *#{variable}\n"
	synthesize << "@synthesize #{variable}\n"
	release << "[#{variable.chop} release];\n"
end

synthesize << release.chomp

`echo '#{synthesize.chomp}' | pbcopy`
print properties.chomp

该脚本的作用是帮助我们添加@property、@synthesize、还有dealloc方法中相应的release。使用方法是选中.h文件中的成员变量,比如我们选中UIButton *aButton;,复制到要添加@property的位置,再次选中,按快捷键执行脚本,@property就添加完成了,然后到相应的.m文件中,在要添加@synthesize的位置按⌘V粘贴,@synthesize也添加好了,同时复制过来的还有[aButton release];,选中这行,剪切粘贴到dealloc方法中,大功告成。使用这个脚本不仅可以快速添加@property @synthesize,同时也避免了变量名写错所造成的一些问题。

XCode技巧之自定义TextMacros

这篇文章一样,以下基本都是从becoming productive in xcode中取得的

===Text  Macros===

添加自己的TextMacros(XCode 3.2.5测试通过,XCode 4 未测试)

在~/Library/Application Support/Developer/Shared/Xcode/下面新建Specifications文件夹,建立与/Developer/Applications/Xcode.app/Contents/PlugIns/TextMacros.xctxtmacro/Contents/Resources文件夹下类似的*.xctxtmacro文件即可

例子

 

(
  {
    Identifier = objc.hello;
    BasedOn = objc;
    IsMenuItem = YES;
    OnlyAtBOL= YES;
    Name = "Hello";
    TextString = "Hello, XCode!";
    CompletionPrefix = "hello";
    IncludeContexts = ("xcode.lang.objc");
  },
  {
    Identifier = objc.property;
    BasedOn = objc;
    IsMenuItem = YES;
    OnlyAtBOL= YES;
    Name = "@property Definition";
    TextString = "@property (nonatomic, retain) IBOutlet <#type#> *<#variable#>;";
    CompletionPrefix = "prop";
    IncludeContexts = ("xcode.lang.objc.interface");
  },
  {
    Identifier = objc.rectmake;
    BasedOn = objc;
    IsMenuItem = YES;
    OnlyAtBOL= YES;
    Name = "RectMake";
    TextString = "CGRect aRect = \n\tCGRectMake(<#x#>, <#y#>, <#width#>, <#height#>);";
    CompletionPrefix = "cgrm";
    IncludeContexts = ("xcode.lang.objc.block");
  }
)

上面的代码添加了三个textmacro:

第一个仅仅是测试用,代码中输入hello即有自动补全提示,补全成Hello,Xcode!。

第二个效果是输入prop,然后 ⌃. 自动补全成@property (nonatomic, retain) IBOutlet <#type#> *<#variable#>;的形式,具体实验一下就知道了。

第三个类似,不多说了。

首先注意一下OnlyAtBOL这一行,在新版本的XCode中如果不加上这一行则textmacro无法生效。具体请看这一段的解释:

There is a bug on TextMacros in the latest versions of Xcode (I encourage you to send a bugreport to Apple as I did)

In fact, only TextMacros that have the value “YES” for the key “OnlyAtBOL” will work. If this key is not present (or set to NO), the macro will not respond to autocompletion.
Actually, the new “OnlyAtBOL” key means “Only at Beginning Of Line”, so the autocompletion for this macro will only work if it is triggered in the beggining of a line. But in fact, quite every macro I need everyday comply to this constraint (ifelse, nslog, nss, …) so this is acceptable.
If you want those macros (that does not work because of the lack of OnlyAtBOL set to YES) to work again, you need to add/override their specifications in your “Application Support” directory (in a custom TextMacro file that override them), until the Xcode team fixes that bug.
 
 

XCode快捷键及小技巧

首先推荐个视频:Becoming Productive in XCode  链接里有个demo版的,完全版是收费的,处于对源作者的尊重,不放出下载地址,各位同学自行解决,推荐有能力的购买一下,还是物有所值的,下面这些基本都是视频中提到的

—Workspace—

⌘N          New project

⌘,             Preference

 

⌘E          Zoom editor in

⌥⇧⌘E       Zoom editor in fully

 

—文件切换—

⌘← →     历史编辑点切换

⌘D          Open Quickly,直接输入文件名,支持查看各种.h .m …

⌘↑          .h与.m切换

 

—File Navigation—

← →         按word移动光标,向右移动到word尾部,向左移动到word头部

⌘← →         移动到该行头/尾,

⇧⌥← →      高亮word

⌘← →      高亮行

⌘↑ ↓            移动到文件头/尾

L               光标所在行放置在编辑器中间

⌘L               Goto(行号)

 

2               打开method列表(应该叫Function Popup)(与系统的Spaces切换有冲突,看自己习惯了)

⌘D              BookMark

4               BookMark列表(BookMark Popup)

⌘M          打开Bookmarks Smart Group

 

⌘F              搜索

⌘G ⌘G   (搜索中)下一个/上一个

⌘F           跨文件搜索

+双击        以浮动窗口形式查看定文档

⌘+双击     查看相应文档

 

—Editing Source Code—

TAB             确认当前补全

  ,        补全列表

/               切换到下一个Placeholder

 

⌘←   →    折叠、展开代码段

⌘↑ ↓         折叠、展开所有method段

⌃⌥⌘F        打开、关闭当前代码段高亮功能

 

⌘/               快速注释、取消注释

⌘T           同时编辑当前文件中所有同一变量名

 

—Refactoring—

⌘J           Refractor对话框

 

—Building and Running—

⌘B              Build

⌘B           Build Result窗口

⌘=  ⌘=    查看warnings、errors

⌘Enter         Build & Go

⌘R           打开Console

⌃⌥⌘R        Clear Console      

 

⌥⇧⌘/        打开文档(这是快捷键么)

 

====Tips====

 

—Workspace–

分组(Group)是任意的,并不会真的产生这个目录

Smart Group很实用

右上角那个搜索是支持通配符和正则表达式的,点击放大镜边上的箭头

 

—File Navigation—

类似列表的东西都是支持键盘输入开头的几个字符来选择相应项目的

#pragma mark [something]     类似分隔线的东西,效果在Function Popup中可见,尤其是 #pragma mark – 的时候

 

// TODO: [something]

// MARK: [something]

// FIXME: [something]

// !!!:

// ???:

以上均可在Function Popup有相应效果

 

Editor部分右上角那个右边带箭头的C按钮 打开可查看Superclass(Navigator popup) 默认无快捷键

 

—Refactoring—

Extract:Refactor那个对话框的另一个功能是Extract 支持自动把当前函数中的一段提取到一个新的函数中,对代码重构很有用。使用方法:选择要提取的部分,按⌘J,选择Ectract,然后修改一下函数名即可,系统已经自动把所需的参数添加好了。 

 

—Building and Running—

Preference 》Debugging 》On Start 选择 Show Console 可以默认打开Console