Pop then push immediately with UINavigationController in iOS 8

在某些业务场景下,需要将一个 ViewController 从 NavigationController 里 pop 后,立即 push 进另一个 ViewController。在 iOS8 之前这不是什么问题,用以下代码即可轻松实现:

1
2
[navigationController popViewControllerAnimated:NO];
[navigationController pushViewController:newViewController animated:YES];

但在 iOS8 上这样无法实现,pop 后不会 push 另一个 ViewController。可以用以下方法实现,思路是直接修改 navigationController 里栈的数据(暂时没有找到其他更好的方法):

1
2
3
4
NSMutableArray *viewControllersInStack = [NSMutableArray arrayWithArray:navigationController.viewControllers];
[viewControllersInStack removeLastObject];
[viewControllersInStack addObject:newViewController];
[navController setViewControllers:viewControllersInStack animated:YES];

分支策略总结

前几日在发布新版本时,发现 SVN 的分支策略有问题,然后读了 SVN 的常见分支模式,依照文章里的模式改变了分支策略,貌似解决了问题。但我对之前用的策略还是念念不忘,经过调查和思考,得出结论:之前用的策略也没有问题,and what’s more,那是 Git 更常用的分支模式,但更适合于开发流程规范、并严格遵循的项目。

在 Git 分支模型的阐述上,Vincent Driessen 的 A successful Git branching model 是一篇很多人认为不错的文章,译文也已经有好几篇了:介绍一个成功的 Git 分支模型Git 分支的最佳实践一个成功的Git分支模型

个人认为,这篇文章阐述的分支模型是非常不错的,但遣词造句上实在有些繁琐,读起来很费劲,不过绝对值得一读。

下面是我对分支策略的总结。

Subversion 和 Git

  1. Git 是分布式的版本控制系统,所有人都拥有一个仓库,而不仅仅是一个 working copy。但依然要有一个远端的中心,称为 origin。相对的,Subversion 是集中式的版本控制系统,仓库只在远端。
  2. Subversion 的三大部分是 trunk、branches、tags,即主干、分支、标记。相对的,Git 没有 trunk 的概念,主干可以认为是 master 分支,也是分支之一。
  3. Git 的分支才是真正意义上的分支。相对的,Subversion 在本质上不存在分支,所谓分支只是目录的拷贝。
  4. Git 在分支与合并方面是简单易行的。相对的,Subversion 的分支与合并总是让人恐惧。

分支模式一(Git 风格)

  1. 长期分支有主分支(master)和开发分支(develop),短期分支有特性分支(feature)、预发布分支(release)、补丁分支(hotfix)。
  2. master 分支,始终 保存每次发布的版本,HEAD 则指向当前发布的最新版本。日常的开发工作不直接在主分支进行。
  3. develop 分支,其中为 下一次 要发布的版本(而不包含下次的下次发布应包含的功能)。
  4. feature 分支,开发某项新特性时使用。这项特性可能在下一次发布时加入软件,也可能在下次的下次发布时才加入软件。也有可能是实验性质的特性,最终可能得以加入软件,也可能效果不好直接抛弃。它应从 develop 分支出,开发完毕后合并回 develop 或直接丢弃。注意并回的时机,是要在 下次发布的版本 中引入这个特性时,而 不是 下次的下次发布时。合并完毕后,应将这个 feature 分支删除。
  5. release 分支,在某一版本准备发布时使用。在 develop 分支已经可以进入发布前的测试时,从 develop 分支出,命名为 release-1.0。测试过程中的 bug 修复,应提交到该分支。测试结束可以发布时,将该分支合并回 master 分支和 develop 分支,并给 master 分支打一个 tag(如 1.0)。然后,将这个 release 分支删除。需要注意的是,合并回 master,是因为这将成为一个新的发布版本;合并回 develop,是为了将 release 分支中对 bug 的修复也合并回 develop 分支,更好的方式是随着测试而不断地将 bug 修复合并回 develop,避免最后才一次性合并。
  6. hotfix 分支,在线上版本出现 bug 需要紧急修复时使用。在 master 分支出,命名为 hotfix-1.0.1。在完成修复代码并通过测试后,合并回 master 和 develop,并对 master 打一个 tag(如 1.0.1)。然后,将这个 hotfix 分支删除。

分支模式二(Subversion 风格)

  1. trunk 主干,日常的开发工作直接在 trunk 进行。
  2. 长期分支有发布分支(release),短期分支有特性分支(feature)。
  3. release 分支,在某一版本准备发布时使用。在 trunk 中的开发已经可以进入发布前的测试时,从 trunk 分支出,命名为 release-1.0。测试过程中的 bug 修复,应提交到该分支。测试结束可以发布时,将该分支合并回 trunk,并给该 release 分支打一个 tag(如 1.0)。注意,这个 release 分支将 永久保留 ,之后的 hotfix 也将在此分支进行。
  4. feature 分支,开发某项新特性时使用。这与分支模式一里的 feature 分支完全相同,唯一不同的是,它从 trunk 分支出,并最终合并回 trunk 或直接丢弃,最后被删除。

Then which one?

生活大爆炸版石头剪刀布

前几日偶然间发现,14年母校的信息学成绩十分了得,在 NOI 2014 中获得了一枚铜牌在 NOIP 2014 中获得了五个一等、两个二等,真是后生可畏,一代更比一代强啊。

好久好久没有做过算法题了。从 NOIP 2003 到 NOIP 2008,从没进复赛,到以总分第二获得一等奖,再到后面几年不堪的表现,NOIP 陪伴了我的整个中学。今天找来 NOIP 2014 的复赛题看了看,发现第一题挺水的,于是做了做。可惜的是 free pascal 在 Yosemite 上竟无法安装,所以决定用 python 来写,顺便再熟悉一下 python。

题目可在这儿查看或自行查找,是 NOIP 2014 提高组复赛 Day1 的第一题:生活大爆炸版石头剪刀布。

题目不难,数据规模也不大,把整个过程模拟一下就好了。唯一值得说的是,将题目中的胜负关系表格补全后形成一个二维数组,就能很方便地在这个数组中查询到每一轮出拳的结果。

rps.py
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
26
27
filein = open("rps.in","r")
arrInput = filein.readline().split()
n = int(arrInput[0])
na = int(arrInput[1])
nb = int(arrInput[2])
arrInput = filein.readline().split()
arrA = [int(x) for x in arrInput]
arrInput = filein.readline().split()
arrB = [int(x) for x in arrInput]
filein.close()
resultTable = [[0,0,1,1,0],[1,0,0,1,0],[0,1,0,0,1],[0,0,1,0,1],[1,1,0,0,0]]
scoreA = 0
scoreB = 0
for i in range(0,n):
solutionA = arrA[i % na]
solutionB = arrB[i % nb]
scoreA += resultTable[solutionA][solutionB]
scoreB += resultTable[solutionB][solutionA]
fileout = open("rps.out","w")
strScore = str(scoreA) + ' ' + str(scoreB) + '\n'
fileout.write(strScore)
fileout.close()

程序很简单。刚开始是读取数据:nnanb 的含义如题,arrAarrB 保存 A 和 B 的策略。resultTable 是前面说过的胜负关系数组,resultTable[甲,乙] 表示甲对乙的得分。for 循环中是对 n 轮出拳进行模拟:用当前的轮数与 A 或 B 的循环节长度取模,就能得到本轮 A 和 B 的策略,用这两个策略去 resultTable 中查询,就能得到 A 和 B 本轮的得分,保存于 scoreAscoreB。最后输出结果。

对 python 了解不多,如果有写的不妥之处,欢迎拍砖。

常见分支模式(译)

##前言

今天在将分支合并回 trunk 的时候,发现之前采用的分支模式不太合适,于是去查阅了 SVN 的用户手册 Version Control With Subversion - Common Branching Patterns,对分支管理有了更全面的认识。

版权声明:本文翻译自 Ben Collins-Sussman, Brian W. Fitzpatrick, C. Michael Pilato 撰写的 Version Control With Subversion。这是一本自由书籍,遵循 Creative Common Attribution License,这里是它的版权协议,本译文同样遵循此协议。

##译文

使用分支 (branch) 和合并 (merge) 的方式有很多,本节介绍了其中最常用的几种。

版本控制是在软件开发中最常用到的概念,所以我们来看一下被开发团队们最常用到的分支/合并模式。如果你在软件开发中并没有用 Subversion 来做版本控制,直接略过这一节就好。如果你是第一次在软件开发中使用版本控制,那请格外注意,因为这些模式通常已被认为是最佳实践。这些模式并不是 Subversion 才独有的,而是适用于任意的版本控制系统。我们以 Subversion 来描述这些模式,让它更容易理解。

###用于发布的分支 (Release 分支)

大多数软件都遵循这样的生命周期:编码,测试,发布,如此循环往复。在这个过程中存在两个问题。第一,在质量保证团队测试稳定版本的同时,开发团队依然需要继续开发新的功能。当软件在接受测试时,新的开发工作不能停顿。第二,开发团队大多都需要支持较老的、已经发布出去的版本。如果在较老的代码中发现了 bug,这个 bug 很有可能也存在于已经释出的版本中。客户想要马上修复它,而不用等待下一个大版本发布时才能解决。

这两个问题借助版本控制可以解决。以下是经典的过程:

  1. 开发者将所有新工作提交到 trunk 上。 每日的改变都被提交到 /trunk :包括新功能、bug 修复等。
  2. 复制 trunk 到一个 “release” 分支。 当团队认为软件可以发布的时候(比如,发布 1.0 版本),/trunk 会被拷贝到 /branches/1.0
  3. 团队继续并行工作。 一个团队开始对 release 分支进行严格的测试,同时,另一个团队在 /trunk 继续新的工作(比如,开发 2.0 版本)。无论哪一个团队发现了 bug,在必要的时候,对于这个 bug 的修复都可以移植 (ported) 到另一个团队。这个过程在某个时间点会停止,然后这个分支将会被“冻结”,以供发布前进行最终测试。
  4. 标记这个分支 (tagged) 并发布。 当测试结束后,为 /branches/1.0 创建一个标记到 /tags/1.0.0 ,作为一个引用快照。这个 tag 中的版本将会被打包并发布给用户。
  5. 这个分支随着时间的推移进行适当的维护。 2.0 版本的开发工作在 /trunk 中继续,修复的 bug 也陆续从 /trunk 中移植到 /branches/1.0 。当修复的 bug 数量累积到足够多的时候,管理层可能决定发布 1.0.1 版本,此时应把 /branches/1.0 创建标记到 /tags/1.0.1 ,这个 1.0.1 的 tag 会被打包发布。

这整个过程将随着软件的成熟而不断反复进行:当 2.0 的工作完成后,一个新的名为 2.0 的 release 分支会被创建、测试、标记,最终发布。几年后,整个 repository 将会是好多个处于“维护”模式的 release 分支,和好多个标志着最终发布给用户的 tag。

###用于新特性的分支 (Feature 分支)

用于新特性的分支 (feature branch) 是在这一章最合适的例子(当 Sally 继续在 /trunk 工作时,你需要开发一个新功能)【译者注:请参见原文在本节之前创设的情境】。这时一种临时的分支,用于进行一些复杂的修改,而不会影响到 /trunk 的稳定。不像 release 分支(需要永久支持),feature 分支的生命历程是:创建,使用它来进行新特性的开发,合并回 trunk,然后被永久删除。它们的作用是有限的。

另外,什么时候需要创建 feature 分支是不一定的,与不同工程的不同情况有关。有些工程从来不使用 feature 分支:所有人都可以自由地向 /trunk 提交。这种系统的好处是非常简单--不需要有人懂得有关分支和合并的知识。缺点是 trunk 的代码经常是不稳定的甚至是不可用的。而有些工程将分支用到了极致:所有的修改都 从不 直接提交到 trunk。即使是最微不足道的修改,也需要专门创建一个短命的分支,仔细审查,然后合并回 trunk。然后这个短命的分支就被删除了。这样可以保证在任何时间 trunk 都绝对稳定并可用,但是导致了大量的流程开销。

大多数工程采用了折中的方式。这种方式坚持 trunk 在任何时间都可以正常编译并通过回归测试。只有当某次修改包含大量提交,可能造成不稳定时,才需要创建 feature 分支。一个好的判断准则是:如果一个功能需要开发者独立工作好几天,然后一次性提交大量的修改(因为要保证 trunk 的稳定,所以要一次性),那这样的修改是不是大到需要进行代码审查了?如果对这个问题的回答是肯定的,那这些修改就应该在一个 feature 分支中进行。这样,开发者就能以递增的方式向 feature 分支中多次提交,而不必担心影响稳定,其他人也可以很容易地审阅到分支中这些代码的变化情况。

最后还有一个问题,如何在工作的进程中保持 feature 分支和 trunk 的同步。正如我们之前所说,在一个分支上工作数周甚至数月是有很大风险的;trunk 上的修改越来越多,与 feature 分支中对应代码行的差距越来越大,从 branch 合并回 trunk 可能会变得噩梦一般。

可以通过时常将 trunk 的变更合并到 branch 来避免这种状况【译者注:合并不是单向的,可以从 branch 合并回 trunk,也可以从 trunk 合并到 branch,后者也被称为“同步”】。制定一个原则:每周一次,将上周 trunk 上的变更合并到 branch 中。

在某个时间点,你会准备将“已同步”的 feature 分支合并回 trunk 中。要做这件事,首先要进行最后一次同步,将 trunk 最新的修改同步到 feature 分支中。完成后,最新的 branch 和 trunk 除了你在 branch 上的修改之外,将是完全一致的。然后就可以通过 reintegrate 选项将分支合并回去了:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cd trunk-working-copy
$ svn update
At revision 1910.
$ svn merge --reintegrate http://svn.example.com/repos/calc/branches/mybranch
--- Merging differences between repository URLs into '.':
U real.c
U integer.c
A newdirectory
A newdirectory/newfile
U .

另外一种理解这种模式的方法是,每周从 trunk 向 branch 的同步可以看作是在 working copy 中执行了 svn update ,最后的合并操作可以看作是在 working copy 中执行了 svn commit 。既然这样,working copy 不就可以认为是一种非常浅的、私有的分支吗?它就是一种特别的分支,每次只有很少的变更。

删掉 Launchpad 里的空白文件夹

Yosemite 的 bug 是真不少,昨晚又发现了一个,Launchpad 里的空白文件夹怎么删都删不掉!(Yosemite 10.10.1)

昨晚手一滑,在 Launchpad 里把一个应用的图标拖到另一个上面了,自然而然地形成了一个文件夹。我随即把他俩都从文件夹里移出来,文件夹自动消失了,看起来没啥问题。等等!重启一下,问题来了,那个文件夹又出现了,而且是空白的,按住 Option 键也没有叉号。如果把一个应用图标再拖进去,然后再拖出来,那个文件夹会再次消失,不过重启之后…抓狂啊!

Empty folder in Launchpad

找到如下方法,在终端中:

1
2
$ defaults write com.apple.dock ResetLaunchPad -bool true
$ killall Dock

如果无效,可以尝试重新启动一下 Mac。

另外,网络上还流传着另一种方法,也可以尝试一下,看上去原理都是重置 Dock 的数据:

把 ~/Library/Dock 目录中的所有文件移入废纸篓,重新启动即可。

最近 Mac mini 经常死机

最近刚买不久的 Mac mini 经常死机,刚买了不到两个月,已经死机至少三次了,而且有两次是死到不得不长按电源按钮强制关机的地步。这让我着实有些郁闷,办公室的 iMac 用了一年半了,印象中只死过一次机。

回忆了一下死机的情形,有两次都是从 Launchpad 打开应用导致的。具体表现是:在 Launchpad 里单击一个应用的图标,Launchpad 本应该打开这个应用并且让自己消失,但此时却没有任何反应,应用没有被打开,Launchpad 也没有消失,继续点击依然是这样的,无论点击什么应用都是这样。

有一次是过了一会儿就死了,鼠标变为旋转的风火轮,等了好久好久都没反应,只能强制关机。

另一次是等了好久后,点击的应用被打开,由于刚才狂点了好多次,所以同时还弹出了好多对话框,提示你这个应用已经被打开了,不能再次打开。但问题还没完,这个好不容易被打开的应用再也关不掉了。Dock 里和强制退出程序列表里,一直有这个程序,强制退出也没用。但是在 Activitiy Monitor 里已经没有这个进程了。于是,如果想关机,系统会不断尝试把这个程序关闭,但是一直关闭不了,只能长按电源…

系统是 Yosemite 10.10.1,难道是 Yosemite 的众多 bug 之一吗?也想不到会是什么原因,唯一能想到可以做的就是修复磁盘权限。

Fix disk authority

Applications/.DS_Store!难道就是因为它的权限出了问题?看上去是很个靠谱的解释。如果过一段时间我没在下面续写新内容,那就是这个原因了,如果有受相同问题困扰的朋友,可以参考。

.DS_Store 的由来(译)

##前言
本文作者为原苹果工程师 Arno Gourdol,正是他主导了 Mac OS X 系统上 Finder 应用的开发。这篇文章为我们阐述了 .DS_Store 文件的由来,是个很不错的计算机历史小文章。原文链接:http://arno.org/arnotify/2006/10/on-the-origins-of-ds_store/

##译文
如果你是一个 Mac 用户,或者你曾经从 Mac 向 Windows 传输过文件,你可能会对一个名为 .DS_Store 的文件很熟悉。但这个名字是从何而来呢?

时间回到 1999 年,我在苹果公司担任 Mac OS X 系统内 Finder 应用的技术主管。在那时,Finder 的代码已有 8 年的历史,代码已经很难以维护。做任何改变都需要做大量的工作,而且常常会影响到两三个看似跟它无关的功能。所以在 Mac OS X 上我们决定,从设计开始,重写 Finder。

涉及到的工作之一,便是分离用户界面和核心功能,也就是后台代码。Finder 的后台会穷举文件、监视文件系统变化、处理元数据,包括图标的位置和文件夹设置。在内部,这两个部分被我们称作 Finder_FE 和 Finder_BE(Frontend 和 Backend)。

但是,没过多久我们就意识到,Finder 以外的应用也很可能需要调用 Finder 后台的功能。所以我们计划在将来某一天,让它成为一个公共的 API。由于我之前曾负责命名了 Icon Services 和 Navigation Services,所以我们决定把它命名为 Desktop Services(在那时,我们还曾考虑过把 Finder 改名为 “Desktop”)。因此,.DS_Store 这个名字,就是 “Desktop Services Store”(桌面服务存储) 的意思。我们在文件名之前添加了一个句点,以保证在 Unix 操作系统上,包括 Mac OS 上,被识别为一个隐藏文件。

个人认为这并不是一个好的名字,我曾希望找到一个描述性更好的名字,但已经太晚了 :)

另外,还有一个至今仍未被修复的 bug,会导致过度创建 .DS_Store 文件。仅当用户更改了视图设置或改变了目录内图标的位置时,这些文件才应创建。可惜的是,事情并没有如此发生,而是当某个目录被访问时,.DS_Store 文件就会被创建。

另外,Finder_BE,也就是 Desktop Services,不再只被 Finder 调用:Navigation Services(打开/保存对话框)现在也会调用它,在 Mac OS 的最初版本中并不是这样的。但现在,Desktop Services 的 API 依然没有完全开放出来。

忘掉 Reveal,使用 Xcode6 来进行视图层次调试

Xcode 6 提供了许多新特性,视图层次调试(Debug View Hierarchy)便是其中很便捷的特性之一。之前调试视图层次,我们需要借助 Reveal、Spark Inspector 等商业软件,如今 Xcode 引入了这一功能,着实令人兴奋。

Debug View Hierarchy

使用也很简单,在 Xcode 中以调试模式运行程序(模拟器、真机均可),然后就可以在 Xcode 底部调试区域找到 Debug View Hierarchy 的按钮(在跳出和模拟位置中间):

Debug View Hierarchy button

或者,也可以在菜单中找到:Debug -> View Debugging -> Capture View Hierarchy。

点击后稍等就会生成视图层次,同时进程会暂停。用鼠标拖动探索一下吧

视图左下方的滑块可以调整层次间的距离,右下角的滑块可以调整显示的层数,下方的按钮可以控制是否显示 clipped 的内容、是否显示 constraints、放大缩小还原等。当然,如果需要诸如 live 修改等高级功能,还是需要借助 Reveal,但 Xcode 中提供的这些基本功能,大多数情况下也足够了。

PS. 如果你在调试区域找不到那个按钮,菜单中的“Capture View Hierarchy” 也是灰色的,可能是你使用的调试设备为 64 位设备(iPhone 5 以上的模拟器或真机),但你的工程属性中并不支持 64 位的原因。可以尝试使用 iPhone 4S 模拟器,那个按钮应该就会出现了。

忽略 "Undeclared selector..." 的 Warning

1
2
3
4
if ([someObject respondsToSelector:@selector(someSelector)])
{
[someObject performSelector:@selector(someSelector)];
}

以上这句代码,除非你在 someObject 的头文件中显式地声明了 someSelector,否则在 Xcode 中会提示警告:

Undeclared selector 'someSelector'

但很多情况下我们并不想去声明它,此时我们可以禁用编译器的此类警告:

1
#pragma GCC diagnostic ignored "-Wundeclared-selector"

这样将会在整个文件内禁用此类警告,也可只在部分代码处禁用,保证编译器依然会对文件内其他代码进行警告检测,避免出现预料之外的 bug:

1
2
3
4
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
// 需要禁用警告的代码
#pragma clang diagnostic pop

Windows server 2008 搜寻不到任何无线网络

在笔记本上安装了 Windows server 2008 R2,却发现搜索不到任何无线网络。3945ABG 无线网卡的驱动程序是正常的,右下角系统托盘里有显示无线信号图标,但点击无线图标后,网络列表里空空如也。

原因很简单,因为系统默认没有打开无线功能,说来也是,服务器一般不需要无线网络。

解决方法也就很清晰了,控制面板 -> 程序与功能 -> 打开或关闭 Windows 功能,在里面找到“无线LAN”功能,把它打开即可。