0%

之前在用Eclipse写Java的时候,有几个常用的快捷键,比如删除当前行,在当前行下面插入空行,向上/下移动当前行等等,到了Xcode里怎么也找不到这些快捷键,一直觉得Xcode自带的快捷键不够强大,直到今天才知道不借助第三方的插件,在Xcode下完全也可以实现这些功能,下面就说一下如何来做。

首先找到Xcode中的自带的配置文件
/Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Versions/A/Resources/IDETextKeyBindingSet.plist
这个文件里配置了一些可以设置快捷键的操作, 使用常用的编辑器打开它(需要root权限)。

然后看看下面这段配置, (来自gist,感谢作者@gdavis )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<key>GDI Commands</key>
<dict>
<key>GDI Duplicate Current Line</key>
<string>selectLine:, copy:, moveToEndOfLine:, insertNewline:, paste:, deleteBackward:</string>
<key>GDI Delete Current Line</key>
<string>moveToEndOfLine:, deleteToBeginningOfLine:, deleteBackward:, moveDown:, moveToEndOfLine:</string>
<key>GDI Move Current Line Up</key>
<string>selectLine:, cut:, moveUp:, moveToBeginningOfLine:, insertNewLine:, paste:, moveBackward:</string>
<key>GDI Move Current Line Down</key>
<string>selectLine:, cut:, moveDown:, moveToBeginningOfLine:, insertNewLine:, paste:, moveBackward:</string>
<key>GDI Insert Line Above</key>
<string>moveUp:, moveToEndOfLine:, insertNewline:</string>
<key>GDI Insert Line Below</key>
<string>moveToEndOfLine:, insertNewline:</string>
</dict>

这个dict是一组可以设置快捷键的操作,里面的key是名称,对应的string是对应的一组操作,从名字本身也可以看出是什么意思,而且也可以根据这些自由装配成自己的别的快捷操作。

  • GDI Duplicate Current Line 复制当前行到下面一行
  • GDI Delete Current Line 删除当前行
  • GDI Move Current Line Up 把当前行往上移动一行
  • GDI Move Current Line Down 把当前行往下移动一行
  • GDI Insert Line Above 在当前行上面增加一空行
  • GDI Insert Line Below 在当前行下面增加一空行(不管光标是否在行尾)

把这段配置放到上面提到的IDETextKeyBindingSet.plist里,放在文件的最后的这两行之前:

1
2
	</dict>
</plist>

重启Xcode,在Xcode菜单中,打开Preferences,选中Key Binding,在右上方搜索GDI, 会出现类似下图的显示,如果没有的话,请检查上面的每步操作。

img

双击右边的空白处,就可以为每个功能设置不同的快捷键,我设置和Eclipse里的一致,感受了下,非常爽,Cooool

Have fun!~

不卖关子,这是一个git repo ,可以从这里下载到WWDC 2013公开的100个视频的英文字幕。
如果觉得有用的话,不妨star一下,或者在微博上@我满足一下我的虚荣心 :-),这都不重要,重要的是一定要坚持看完这100个视频

我发起这个项目以及抓取到这些字幕的的原因是这样的,一个是英语的听力太差,基本上听不懂苹果的传道士们在视频中说的是什么,没有字幕真是很难受,然后是发现在iPad上使用WWDC这个App看视频的时候是有字幕的,但在iPad上看屏幕不够大,看起来也很费劲。就想既然在iPad上有字幕,一定有办法抓取出来,于是就开工,用burpsuite之类的抓Http请求包的App很容易就能探测到字幕文件的地址,在准备写代码的时候,Google了一下,发现一个python写的gist正是做这个的,于是就用这个脚本把一部分视频的字幕下载下来,自己又现学了点ruby写了个gist脚本来把分散的字幕文件按照顺序合并起来。 刚开始下载的比较慢,因为这个脚本是单线程的,后来自己改了一下,分10个线程,每个线程下载10个视频的字幕,这样就快很多,这个代码因为比较简单,就没放出来,有兴趣的童鞋自己也可以实现。

另外@lexrus同学的这个gist里提供了所有视频的HD和SD的版本,以及文件序号和视频名称的对应关系,可以直接放在迅雷里下载,完了再配上字幕,可以像欣赏好莱坞大片一样的欣赏WWDC2013带来的新技术盛宴了!

事情是这样的,前几天电脑崩溃,硬盘数据全部丢失,重装系统和Xcode之后,从Develop Center的Certificates里重新下载证书,安装到新电脑上,在真机上运行时,提示报错:”A valid signing identity matching this profile could not be found in your keychain”, 按照字面意思查了一下,是因为本地KeyChain里丢失了private key的缘故,得到的结论是要么从之前的备份中恢复,或者重新生成新的证书。遗憾的是我之前并没有保存备份,无奈只好重新生成。一个问题马上出现在脑海,就是重新生成新的证书是否会对线上的App产生影响。查了一下官方的文档,发现有这部分详细的说明,消除了我的顾虑,内容如下(参考2):

Important: Members of the Standard iOS Developer Program can be assured that replacing either your developer or distribution certificate will not affect any existing apps that you've published in the iOS App Store, nor will it affect your ability to update those apps.

Notes before beginning:

Replacing your distribution certificate won't affect your developer certificate or development profiles.

Similarly, replacing your developer certificate won't affect your distribution certificate or distribution profiles.

Replace only your development certificate if you are troubleshooting an issue running your app on device through Xcode.

Replace only your distribution certificate if you are troubleshooting an issue creating, submitting or installing a distribution build.

After replacing your certificate(s) you are required to update and reinstall any provisioning profiles that were bound to the old certificate.

搞清楚问题和解决办法,就开工:

  1. 首先在iOS Provisioning Portal里revoke掉当前失效的Certificates,并创建一个新的Certificates(参考[2])
  2. 通过import和export Developer Profile备份和恢复(参考[3]和[4])

Links:

  1. iOS Provisioning Portal
  2. How do I delete/revoke my certificates and start over fresh?
  3. Exporting and Importing Developer Profile
  4. Transferring Your Identities

Version & Build 号

Image1 icon

今天对Xcode里iOS的版本号又有了新的认识,一个叫做Version,一个叫做Build,这两个值都可以在Xcode中选中target,点击“Summary”后看到。 Version在plist文件中的key是“CFBundleShortVersionString”,和AppStore上的版本号保持一致,Build在plist中的key是“CFBundleVersion”,代表build的版本号,该值每次build之后都应该增加1。这两个值都可以在程序中通过下面的代码获得:

1
[[[NSBundle mainBundle] infoDictionary] valueForKey:@"key"]


### Archive后自动增长build号 除此之外,如果我们想在Archive后build号自动增长,就可以使用到Xcode的run script来实现,步骤是
  1. 选中项目的target,点击“Build Phases“
  2. 点击右下角的”Add Build Phrase“,选择”Add run script“,会产生一个新的Run Script项
  3. 拖拽新生成的Run Script项到最上面
  4. 点开该项,copy下面的shell代码进去,代码来自[这里](http://stackoverflow.com/questions/9855955/xcode-increment-
    build-number-only-during-archive?answertab=active#tab-top),如下图所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if [ $CONFIGURATION == Release ]; then
echo "Bumping build number..."
plist=${PROJECT_DIR}/${INFOPLIST_FILE}

#increment the build number (ie 115 to 116)
buildnum=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${plist}")
if [[ "${buildnum}" == "" ]]; then
echo "No build number in $plist"
exit 2
fi

buildnum=$(expr $buildnum + 1)
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "${plist}"
echo "Bumped build number to $buildnum"

else
echo $CONFIGURATION " build - Not bumping build number."
fi

这段shell脚本的意思就是说,如果当前的配置是Release(Archive时该值为Release,直接在模拟器上运行是Debug),就设置build值为当前build值+1, 否则什么都不干。

这样在build的时候就会看到build号会自动加1的,想看build时输出的信息,可以通过”View -> Navigators -> Log”来查看最新的build时产生的log。


Ref:

  1. Concurrent Debug, Beta and App Store Builds
  2. stackoverflow: Xcode-Increment build number only during ARCHIVE?

Have fun!

KVC

KVC是Key-value coding的缩写,是一种通过key-value的方式获取对象属性的机制。
这个key是一个String的唯一标示符,这个key的name约定是必须是ASCII码、小写字母开头、中间不能有空格。

让一个类实现KVO的方式是遵循NSKeyValueCoding这个协议,该协议中定义了2个方法:valueForKey: and setValue:forKey:.这两个方法用来通过key访问和获取对象属性。

More about KVC

KVO

Apple Document about KVO

KVO的实现方式:isa-swizzing

Lightweight Key-Value Observing

在iOS开发中,UINavigationController是很常用的Controller,对它的一般操作就像操作一个栈,push和pop。但也经常会遇到pop和push无法优雅的完成的操作,比如退回到中间的某个VC上,或者在第一个VC之前添加一个VC等,更甚者要重新构造整个VC的顺序,这时候setViewControllers方法就排上用场了,它使对VC栈的操作不再局限于push和pop,而是构造整个VC栈并应用到当前的UINavigationController中,这个方法支持iOS3.0+,放心使用。


#Sample

1
2
3
4
5
6
NSMutableArray * viewControllers = [self.navigationController.viewControllers mutableCopy];
[viewControllers removeLastObject];
[viewControllers addObject:newController];

[self.navigationController setViewControllers:viewControllers animated:YES];
// [viewControllers relase] // if non-arc

感谢 Allen(Weibo) 提供的代码和思路




#说明
下面这段摘自Api文档

You can use this method to update or replace the current view controller stack without pushing or popping each controller explicitly. In addition, this method lets you update the set of controllers without animating the changes, which might be appropriate at launch time when you want to return the navigation controller to a previous state.

If animations are enabled, this method decides which type of transition to perform based on whether the last item in the items array is already in the navigation stack. 

1.If the view controller is currently in the stack, but is not the topmost item, this method uses a pop transition; 
2.if it is the topmost item, no transition is performed. 
3.If the view controller is not on the stack, this method uses a push transition. 

Only one transition is performed, but when that transition finishes, the entire contents of the stack are replaced with the new view controllers. For example, if controllers A, B, and C are on the stack and you set controllers D, A, and B, this method uses a pop transition and the resulting stack contains the controllers D, A, and B.

Have fun!

Image1 icon

最近项目要重构,首当其冲的就是代码结构,因为很多原因之前很少考虑代码结构的事情。终于要抽出一部分时间来重构这个项目,首先是整个项目的结构和代码逻辑不太符合MVC,又顺便了解了一下iOS里的MVC模式的概念。首先MVC模式不光定义了每一部分在整个应用中扮演的角色,也定义了各个部分相互沟通交流的方式。每一部分都扮演着不同的角色,分工明确,降低耦合,减少依赖,使得每一部分都能够复用,这也是MVC模式的意义和目的所在。下面就简单描述一下MVC模式里对每一个角色的职能和责任。

Model

Model层对象应该是封装了一定的数据规范,并且定义了管理和处理这些数据的逻辑和计算。简单说就是Model对象不仅定义了数据结构,还要包括对数据结构的操作和处理逻辑。比如从网络、sqlite、UserDefault里获取需要在View里展现的数据以及存入用户感兴趣的数据等等。其实Model里是包含业务逻辑的,这一点和Web开发差异很大,之前在用Java开发Web程序时使用MVC,M就是POJO,只包括定义数据结构,不包含对这些数据的处理(处理的部分放在一个叫Service层里),也称之为贫血模型。相对应的充血模型就类似这里的M,是包含对数据的操作和处理,ROR里的ActiveRecord就是这样的。

View

View层的理解就很简单了,就是用户能看得见的东西,比如UIKit里的各种系统自带控件等。View对象应该知道如何把自己展示给用户并且对用户的操作做出回应。View层对象的主要用途在于展示出应用的Model层数据并且允许用户通过交互修改这些数据。

Controller

Image2 icon

Controller层对象相当于一个中间人,负责协调应用中的View层对象和Model层对象的关系,也是View和Model相互沟通的媒介。除此之外,Controller还负责组织和协调应用中的任务以及管理其他对象的声明周期。

相互的沟通

Image3 icon

Model层不直接和View沟通,当Model层对象改变(比如通过网络获取到了新的数据),它会通知Controller对象,Controller对象收到通知更新对应的View。当View层有改变(比如用户通过交互创建或修改了数据,称为User Action),View会通过Controller对象去创建或修改Model层的数据。 Model层和View层是相互不知道对方的,它们的沟通是通过Controller这个中间人来协调处理。

Jekyll默认的社会化评论组件是disqus,第三方SNS是facebook,twitter等,不方便大陆用户使用,发现国内也有类似的社会化评论组件,比如友言等,经比较发现友言更简单易用。

替换的整个过程很简单,分为两大步:
首先要注册一个友言的账户,点击获取代码,就能获得一段和你用户相关的js代码。类似下面这样:

1
2
3
4
<!-- UY BEGIN -->
<div id="uyan_frame"></div>
<script type="text/javascript" id="UYScript" src="http://v1.uyan.cc/js/iframe.js?UYUserId=YOUR_USER_ID" async=""></script>
<!-- UY END -->

然后要切换到本地来,由于Jekyll的评论组件是插件式的,很方便修改,分为下面2个步骤

  1. 修改_config.yml文件中comments:下的provider:的值为custom(默认是disqus)
  2. 在_includes目录下新建一个目录custom,在custom目录下新建一个文件comments,文件的内容就是上面从友言获得的那段代码。

push到GitHub,刷新页面查看效果吧

这么做的原理很简单,看一下youname.github.com/_includes/JB/comments文件的
看最后一个when语句,当site.JB.comments.provider的值为custom时,就加载custom/comments文件,那么其实site.JB.comments.provider的值就是刚才在_config.yml中设置的那个provider,这样就能说的通了。

Have fun!

FMDB是Objective-C上操作Sqlite的开源库,与原生的操作sqlite数据库相比,有以下几个优点:

  1. 操作方便、简单、代码优雅,易于维护;
  2. 线程安全,用着更放心,很少出现过锁死数据库文件以及Crash的现象。

FMDatabase不是线程安全的,一个FMDatabase对象一定不能在多线程中使用,为了保证线程安全,可以在FMDB中采取下面两种方式:

  1. 每个线程都创建一个FMDatabase对象,使用之前打开连接,用完关闭销毁;
  2. 使用FMDatabaseQueue来保证线程安全,一个FMDatabaseQueue的对象可以在多线程中共享使用。

使用FMDatabase时,一般这样来做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建一个 FMDatabase的对象
FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
//判断db是否打开,在使用之前一定要确保是打开的
if ([db open]) {
//使用FMDatabase操作数据库
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
//retrieve values for each record
}

//关闭
[db close];
}
db = nil;

上面的这段代码是使用FMDatabase操作数据库的一个典型的使用方式,可以看到,其实我们关注的只是使用它来对数据库进行增删改查的操作,却每次都要写这些打开和关闭的操作,代码也显得臃肿,bad smell。用过Java中著名的Spring框架的同学都记得里面对数据库操作提供了一个Template的机制,比如JdbcTemplate、HibernateTemplate等,使用回调函数非常优雅的分离了创建连接、关闭连接和使用数据库连接操作数据库,下面就来模拟这个实现。
首先做个抽象,在上面代码的真正的逻辑中,我们只要拿到db变量就能满足我们的需要了,那么我们就把这一块抽象出来,在这里我们使用oc里的block来实现回调功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//创建一个工具类TWFmdbUtil
@implementation TWFmdbUtil
+ (void) execSqlInFmdb:(void (^)(FMDatabase *db))block {
NSString *dbPath = @"dbpath"; //sqlite数据库文件的路径
//创建一个FMDatabase的对象
FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
//使用之前保证数据库是打开的
if ([db open]) {
@try {
block(db); //调用block来回调实现具体的逻辑
}
@catch (NSException *exception) {
//处理异常,也可以直接抛出,这样调用者就能捕获到异常信息
NSLog(@"TWFmdbUtil exec sql exception: %@", exception);
}
@finally {
[db close]; //如果[db open]就要保证能关闭
}
} else {
//如果打开失败,则打印出错误信息
NSLog(@"db open failed, path:%@, errorMsg:%@", dbPath, [db lastError]);
}
db = nil;
}
@end

现在使用的时候就能够像下面这样来实现了:

1
2
3
4
5
6
7
[TWFmdbUtil execSqlInFmdb:^(FMDatabase *db) {
//处理业务逻辑
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
//retrieve values for each record
}
}];

这样的代码看起来是不是优雅多了呢?我们无需关心数据库的创建和关闭操作,只需要关心我们的业务逻辑就可以了。

历史总是惊人的相似,FMDatabaseQueue的使用就是采用这样的方式来处理的,来看一段fmdb主页上提供的一个例子:

1
2
3
4
5
6
7
8
9
10
11
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];

FMResultSet *rs = [db executeQuery:@"select * from foo"];
while ([rs next]) {
//...
}
}];

更多实例请移步FMDB在GitHub上的主页
或者访问@唐巧_boy 关于FMDB的这篇文章

Have Fun!

使用diff查看文件更改信息

1
2
3
4
5
6
7
8
9
10
#查看未暂存文件的变化(与最近一次的暂存/提交比较)
$ git diff
#查看已暂存文件的变化(与最近一次提交比较)
$ git diff --cached
#查看与版本库中任一版本的变化
$ git diff 3e4e
#查看任意两个版本间的变化
$ git diff 3e4e 5d5a
#具体到某个文件
$ git diff 3e4e 5d5a index.md

查看任意版本下的某个文件

1
2
#查看某个版本下某个文件内容
$ git show i5d5a index.md