用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的发生。

–以上–

Cocoa: Drag And Drop 简单实现

先吐个槽:iOS开发在国内还是蛮火的,但是Cocoa的中文资料是在是很少,虽然国内有那么几个论坛,但是总体实力与神马StackOverflow啥的还是差多了,一些问题还是到国外的网站查才是王道。

今天遇到了这样的问题:如何实现向ImageWell中拖入文件,然后显示文件的图标并取得文件路径。

一番查找后没有太大收获,决定回归文档,如果你有同样的问题强烈建议仔细阅读一下文档中的Introduction to Drag and Drop,虽然都是英文,但是其实内容并不多,基本一个小时左右完全可以看完,另外结合一下Cocoa DragAndDrop这个官方样例(文档里也有),保证你能弄清楚Drag and Drop的实现方法。

废话不多说了,喜欢自学的就不用往下看了,懒得看文档的可以听我唠叨两句,但是还要结合文档才能真正弄懂,事先说明一下,我也只是个业余Cocoa爱好者,属于需要什么看什么的类型,所以如果犯了什么错误还请留言指正。

贴代码(点击右边箭头展开)(为了简便以下代码仅实现取得文件路径功能)

 


#import 

@protocol DragAndDropImageViewDelegate

- (void)dragFinished:(NSString *)filePath :(int) tag;

@end

@interface DragAndDropImageView : NSImageView {
	id  delegate;
}

- (id)initWithCoder:(NSCoder *)coder;

@property(nonatomic,assign) id  delegate;

@end

 


#import "DragAndDropImageView.h"

@implementation DragAndDropImageView

@synthesize delegate;

- (id)initWithCoder:(NSCoder *)coder {
	NSLog(@"initWithCoder");
	if (self = [super initWithCoder:coder]) {
		[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
	}
	return self;
}

- (NSDragOperation)draggingEntered:(id )sender {
	NSLog(@"draggingEntered");
	NSPasteboard *pboard;
	NSDragOperation sourceDragMask;

	sourceDragMask = [sender draggingSourceOperationMask];
	pboard = [sender draggingPasteboard];

	if ([[pboard types] containsObject:NSFilenamesPboardType]) {
		if (sourceDragMask & NSDragOperationLink) {
			NSLog(@"return NSDragOperationLink");
			return NSDragOperationLink;
		}
	}
	return NSDragOperationNone;
}

- (BOOL)performDragOperation:(id )sender {
	NSLog(@"performDragOperation");

	NSPasteboard *pboard;
	NSDragOperation sourceDragMask;
	int tag = [self tag];

	sourceDragMask = [sender draggingSourceOperationMask];
	pboard = [sender draggingPasteboard];

	if ([sender draggingSource] != self) {
		if ([[pboard types] containsObject:NSFilenamesPboardType]) {
			NSLog(@"ready to modify");
			NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
			NSString *filePath = [files objectAtIndex:0];
			[delegate dragFinished:filePath :tag];
			// modify here to continue
		}
	}
	return YES;
}

@end

@protocol如果没有相应需求可以忽略。

简单解释一下吧,initWithCoder是给InterfaceBuilder用的,其中的registerForDraggedTypes作用是声明Drag and Drop响应的文件类型,当有文件拖入时就会调用draggingEntered方法,该方法中判断是不是要对拖动进行响应,如果响应则继续进入performDragOperation方法,完成处理。还有些中间过程以及详细的解释,以上代码没有涉及,详情请参阅文档。

–以上–

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快捷键及小技巧

首先推荐个视频: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

[AppleScript] 小技巧:执行sudo的方法

AppleScript是个强大的好东西,虽然我只是用它来执行一些命令行操作(呃,实际上我还没学怎么进行别的操作),好了废话不多说了,直接进入主题。

在AppleScript中执行终端操作的方法:

    do shell script "SOME COMMANDS"

很简单吧,如果要有管理员权限怎么做,人们自然想到要这样:

    do shell script "sudo SOME COMMANDS"

但是运行一下会出现这个提示:

    AppleScript 错误

    sudo: no tty present and no askpass program specified

解决方法很简单,不要直接把sudo加到里面,而是改成这种形式

    do shell script "SOME COMMANDS" with administrator privileges

这样运行的时候就会弹出那个提示输入管理员密码的那个对话框,sudo的问题就解决了

 

[教程]共享本机代理给其他设备

由于某些众所周知的原因,本文风格可能有些委婉,还请各位谅解。

     有些代理程序只能给本机使用,比如那些默认地址是localhost:port这样的,要把这种只针对本地的代理共享给其他设备尤其是iphone,touch,ipad,android等移动设备就有些麻烦了。笔者认为其中一个可行的方法就是搭建一个服务器,比如apache之类的,再设法把网络请求通过本机的代理发送(大概就是这个意思吧,我也描述不太好)。但是我只实现了搭建服务器这一块,不知道怎么实现后面那部分,要是高手看到这还请多多指教。

    好了言归正传,既然有难度的弄不明白,总有简单的方法,只需要Charles Web Debugging Proxy这个软件即可实现。这个软件夸Mac Linux Windows三大平台,有30天试用期,希望广大同学多多支持正版。

    软件针对本文的使用方法:菜单:Proxy  >>  External Proxy Settings…,勾选 Use External Proxy Serves,在下面即可设置代理,注意到这里还可以设置socks代理,因此可以通过Charles把代理共享给不支持socks代理的设备。

    在Proxy >> Proxy Settiongs下面可以设置对外的端口,默认是8888,设置好之后就可以把代理共享给其他设备了。

Have Fun

-以上-

教主生日总结

  1. The new mac book pro:感觉升级不是很大,有试水的嫌疑,完全没有3GS升级到iPhone4的感觉。USB2.0、FireWire、Thunderbolt在同一台机器上感觉总有些多余。
  2. Thunderbolt:以苹果和intel的实力,也许可以直接干掉USB3.0,成为新一代标准接口,毕竟Mac市场占有率越来越高了。
  3. FaceTime:正式版可以在Mac App Store上下载了,但是居然要0.99刀,实在不厚道,不知道测试版还能用多久。
  4. 10.7 Lion:官方页面更新了如下特性:
  • AirDrop, 无需设置,就可以通过无线从一台 Mac 上拷贝数据到另一台 Mac 上;
  • Versions, 自动保存先前版本的文档,你随时可以浏览、编辑、恢复先前的版本;
  • Resume, 无需退出您的程序,下次打开时将还原最后关闭时的状态;
  • Auto Save,自动保存你的文档,您无需时刻备份;
  • 全新的 FileVault,高性能的本地、外置磁盘加密,支持直接从 Mac 上擦除数据;
  • Mail 5:UI更新,更加接近iOS系列
  • Mac OS X Lion Server,设置服务器更简单,并支持管理 Mac OS X Lion, iPhone®, iPad 和 iPod touch® 设备;
  • 另外,开发者可以用 promocode 从 Mac App Store 下载 Lion 的预览版本。

总结:MBP更新似乎不太令人满意,Lion看来还有很大发展空间,期待早日放出正式版。最后祝教主身体健康。

以上

(转载)屏幕范围快照”Command + Shift + 4”功能补全说明

 

日本わかばマークのMacの备忘录网站针对Mac作业系统屏幕范围快照功能做了补充介绍,向来Mac使用者除了熟知组合键”command + shift + 3”全屏幕快照功能外,拖曳屏幕范围的快照功能”command + shift + 4”也相当实用,这里就”command + shift + 4”的补充功能进行说明。

 

 

相关的图示范例请参考原文,只要熟记组合键的应用,对于屏幕撷取的需求有相当助益;友站ApplePlan另整理了相关的快照进阶技巧,亦欢迎参考。

基本功能

     透过组合键,拖曳游标范围后,就能针对范围进行屏幕快照。

     透过组合键,按一下空白键后,再点按一下滑鼠键(click),即能针对单一执行中的视窗进行快照。

辅以option键

      再按下组合键后,先按住option键,屏幕拖曳范围的起点就能更灵活定位。

辅以shift键

      再按下组合键后,请先不要放开拖曳中的游标,确定范围的横(宽)或纵向(高)尺寸后,这时只要按住shift键,横向移动游标即能针对同样的高度延伸屏幕快照范围;反之,按住shift键后,纵向移动游标,即能针对同样的宽度,延伸所需的萤幕快照高度。

辅以space空白键

    按下组合键,并且确定屏幕快照的范围尺寸后,这时若按住空白键,屏幕快照范围能提供固定尺寸、随意的移动。

 

Mac下为iTunes U添加字幕的方法

iTunes U的介绍等废话就不多说了,直接进入主题,使用一个叫iSubtitle的软件即可搞定,当然你还要有相应的字幕文件才行。经笔者测试,虽然该软件输出的文件为mov格式,但是itunes仍然可以正确识别并分类。支持字幕开关,不损坏原来的文件。同时支持iPod等设备,字幕效果也很赞,输出速度快,不支持批量操作算是一大遗憾,总体来讲还是很不错的。对于iTunes上下载的正版电影笔者没有条件测试,据说是不行的,有兴趣的可以尝试,软件的具体使用方法参见帮助,下载地址(2.3中文版),有问题请留言。

(END)