全站开启SSL

之前不开启SSL,是因为我发现伟大的墙很喜欢墙带有SSL的网站,因为他们没法监控内容就只能封IP了。所以我希望他们对待我的网站,可以像对待开启加密以前的维基百科一样,使用关键词重置,而不是封IP。但实际上墙发展到现在,越来越简单粗暴了,大多数网站要么全墙,要么全能访问。这估计是因为旁路重置要消耗大量的计算和网络能力吧。像我这种垃圾网站,他们一来看不上,要是看上了,也不会浪费计算资源在这个小网站上,必然是直接墙IP了。所以就干脆开SSL吧。

奶粉,代购,自信,尊严

今天发了一条状态引起了大家的不满。这确实是我说的不对,导致大家都情绪化使得问题本身没有收到重视。现在请心平气静地听听我对奶粉代购的看法。

我还是在国内的时候,同事们得知我要去澳洲留学的事,便有希望我帮他们代购一些奶粉的。其实在这里做代购真的很赚钱,一箱奶粉卖出去价格能翻番。然而我坚持不做奶粉代购,除了我学业太忙之外,我认为这涉及到民族尊严,而这是我不能跨越的底线。一年之前,澳洲的奶粉荒并没有这么严重,但在今天,澳洲本地人对我们中国留学生的不满已经相当严重了。当我在超市里看到用中文字写着“限购6罐”的提醒时,竟觉得无比丢脸。

没错,这是合法的,从纯经济的角度来说,这就是正常的市场经济供需关系。我想作为澳洲的奶企来说,中国的消费需求就是天大的好消息(这也许是为什么不久前中国宣布二胎政策之后,澳洲乳业的股票大涨的关系)。但在经济之外,还有一些别的东西。

我们不妨换位思考。如果中国的奶粉质量非常好,而澳洲的奶粉质量有很大的问题。这时中国有很多澳洲留学生将中国超市的奶粉一抢而空。作为一个中国家长的你,原本下班在家门口就能买到的奶粉,不得不跑遍全市的商场去买。这时中国家长心里会怎么想呢?

在一个法制比较健全的国家,这样的问题可以通过法律来解决,比如颁布限购的政策。澳洲和很多其他遇到类似问题的国家确实是这么做的。然而问题解决了之后,有一样东西没了,那就是中华民族的尊严。因为在澳洲人心中,我们中国留学生俨然已经成了麻烦制造者。

当然,这不能怪中国家长。为了自己的孩子,他们并没有错。问题在于国人对本国食品安全的信心彻底崩塌了。我认为这是中国当下最严重的问题之一。

我买过日本货,澳洲货。它们给我印象最深的就是本国人对本国产品的那种信心。日本食品包装上很多都印有“国产”两个字。(补充一件趣事,又一次我跟我妈逛久光,我买了一盒纳豆,上面印了“国产”两个字。我妈立即反对说国产的东西不能吃。我说这“国产”是日语,这里的国是日本国)。在澳洲,很多商贩宁愿打擦边球,也要贴上Product of Australia的标签。然而在中国,情况却截然相反。进口食物成了安全的代名词。多少人为了自己的孩子能够喝到安全的奶粉而去拜托身在国外的好友。这一切在我看来觉得非常可悲。吃时人类生活最基本的要求,如果最基本的食品安全都无法保障的话,就很失败。中国成功地解决了温饱问题,却在食品安全问题上栽了跟头。而解决食品安全问题的难度,我认为可能要大于解决温饱问题的难度。一个企业信誉受损要恢复非常困难,如果整个产业信誉不再,真不知道什么时候国人能恢复对它的信心。

到底为什么会变成今天这样子?这是值得所有人思考的。之前有很多可笑的说法,说中国人没有信仰,西方人有信仰之类云云,都是屁话。我认为关键还在法制欠缺。不是说没有相关法律,而是执法力度太小,无法起震慑作用。这便使得中国进入了一个互害社会。食品安全是其中一个突出的问题,经济全球化又使得中国的问题或多或少影响到了其他国家。

作为一个中国留学生,我特别希望自己的民族能够受到尊重。但尊重不是凭空产生的,不是“你鄙视我,我就敌视你”能解决的。要受人尊重,必先自强,需要有足够的自信和真正的强大。很可惜我们并没有真正拥有这份自信。

我希望看到的是,有一天国人能争相购买自己的奶粉,并且认为这是安全的。而不是像现在一样,香港人骂我们,我们就去澳洲买。澳洲人骂我们,我们就去欧洲买。到那时候,可能我们才能重拾那份自信,重新被人尊重。

在OS X上使用OpenMP和MPI

自从Xcode的编译器从GCC变成clang之后,在OS X上使用OpenMP和MPI就变得很麻烦了。之前查了一下,貌似有clang-omp这个项目,但是配置起来似乎很麻烦。所以干脆直接在电脑上装了GCC跟OpenMPI。

首先你需要确保已经安装了Homebrew。在终端输入 brew -v 查看Homebrew的version。如果提示未安装,输入

来安装Homebrew(更多信息请访问http://brew.sh/)。

在使用Homebrew安装之前,请运行 brew doctor 来确保你的配置没有问题,如果有问题,请按照提示一项一项修改。

接下来输入

进行GCC的安装。其中 --without-multilib 是必须要有的,因为multilib在OS X上跟OpenMP冲突,可能导致OpenMP无法使用。GCC的安装过程比较缓慢,因为需要从零开始编译安装。一般编译过程在1~2小时左右,视机器情况而定。

Homebrew安装的GCC存放在 /usr/local/bin/gcc-5 中(GCC5.2.0版)。此时,系统中依然有 /usr/bin/gcc ,然而这个GCC是clang的链接。所以在编译时,需要使用类似 gcc-5 xxx.c -o xxx -fopenmp 进行编译,而不是使用默认的gcc。

接下来要安装OpenMPI,同样使用Homebrew

在使用mpicc编译时,需要注意,如果不做任何操作,mpicc会使用默认的gcc,也就是clang的链接来编译,使得编译出错。所以在编译之前需要设置环境变量

这样,mpicc就会使用gcc-5来编译了。

UPDATE

如果你需要编译C++文件,请使用 MPICXX 进行编译,并且指定环境变量为

UPDATE2 使用mpi4py

首先,安装python3。因为学校的Spartan集群只有python3环境有mpi4py。

使用homebrew安装的python3自带pypi。接下来就可以安装mpi4py了。

首先要确保你已经安装了openmpi (见上)。然后运行

就能安装mpi4py了。

测试:

如果import mpi4py之后没有报错的话,就说明装完了。

另外注意:

你要使用python3运行你的程序,请使用:

记住是python3不是python! 记住是python3不是python! 记住是python3不是python!

XcodeGhost以及下载工具安全

这几天iOS界的一件大事就是XcodeGhost了,因为这个好几个大厂都中招了。估计是iPhone推出之后iOS界的第一大安全事件了。

很多人在讨论这件事情的时候都怪程序员安全意识薄弱,干嘛去非官网下载呢?但我觉得这么多大厂的程序员都去百度网盘下载Xcode不太正常。至少像我这种水平的人平时找资料什么都是去官网或者StackOverflow,下载软件更是认准原版。要是这些大厂的程序员找资料什么的还混迹于国内论坛的话,我觉得国内的iOS界已经没救了。所以我不太相信这些大厂程序员会这么做。

我觉得导致XcodeGhost传播如此广的罪魁祸首,很可能就是迅雷。当然我也没什么直接证据证明,因为这个过程是不可再现的。迅雷本身声明说自己服务器里的副本是原版,不是他导致的。但是如果你相信他们的声明的话,跟相信“中国互联网是开放的”这样的说辞没什么两样。这一切的一切都得从苹果Xcode下载的特殊性和迅雷的加速算法说起。

苹果对下载开发工具是有严格的限制的,只有拥有开发者账号的人才能下载DMG版本的Xcode。比如Xcode 6.4的下载URL是http://adcdownload.apple.com/DeveloperTools/Xcode6.4/Xcode_6.4.dmg,如果你把这个URL用wget或者curl下载,得到的是一个“未认证”的错误。这是因为adcdownload是需要你登录时使用的cookie做凭证才能让你下载的。所以三方下载工具,包括迅雷,在没有使用镜像和P2P加速的情况下是不可能下载到这个原版的文件的。那么问题就来了,现在给你一个URL,但你从一开始就无法下载,并且你不知道它的checksum。接着给你ABCD四个文件,然后说,ABCD四个文件中,有一个是那个URL下载下来的,其余都是假冒的。那么请问你如何判断哪一个是原版?答案是不可能判断。因为你根本没有一点原版文件的信息。

source

source2

上面两个截图是我在下载Xcode 6.4的时候,迅雷给出来的数据。可以看到迅雷下载原始文件失败了,只能从镜像中下载。根据上面的推理,我根本无法知道镜像中的Xcode是否原版,在我无法得到任何比特的原始文件的情况下。我只能猜。比如说我的网络里面大多数人都拥有这个checksum的文件,那么这个文件很可能是原始文件。或者qq.com这个域名看起来靠谱,这个可能是原始文件。这个时候,任何结论都是不确定的,只是一个概率问题。而黑客很有可能在这里面做手脚。于是会出现一个很可怕的情况:你在迅雷上输入的是苹果官方的Xcode下载地址,但你下载下来的却是一个XcodeGhost!

XcodeGhost闹得这么大,我认为是多方因素导致的。首先GFW以及国际带宽慢导致了在官网上下载Xcode非常慢。然后苹果的官方下载点不支持断点续传,Xcode又是一个3gig的大家伙,AppStore又没有提供增量更新。这两个因素导致了人们倾向于使用迅雷之类的下载工具下载DMG文件。迅雷之类的加速工具又无法确认镜像文件是否原版文件,以至于会出现输入原版地址,下载到一个修改版文件的情况。然后苹果又不提供DMG文件的checksum,所以无法对照下载下来的文件是否是真的。这些事情连起来,才导致了现在这种情况。

虽然现在没有证据证明迅雷是使得XcodeGhost传播得如此广泛的罪魁祸首,但至少可以知道在无法下载原始文件的情况下,开启镜像下载是有安全风险的。这次事件是很有意义的,它让我们知道了对生产的每一个环节都必须做好安全防护,不仅仅是对编译器,甚至是对下载器也要确保它的可信性。

谈什么盛世

前不久阅兵,一时间很多国人都爱国心爆棚,觉得中国是天朝上国了。甚至还有去攻击那些不看阅兵式的台湾艺人。在这里我不想多评论什么,只是我觉得要泼一盆冷水。我不说什么闵煮法治之类的无休止争论的话题,只是说食品安全的问题,中国就跟别的国家差远了。

我来到澳洲之后,澳洲人给我的一个很深的印象就是对本国产品的热爱。
product-of-australia
这个印有小袋鼠的绿色标志是“澳洲产品”的意思。在超市里,很多很多的产品都印有这个标志。有些东西,除了product of Australia的标志之外,还印上了澳洲的国旗,让人远远看到就知道是澳洲产品。

跟中国人追求进口产品的心态恰恰相反,澳洲人追求的是本国产品。因为本国产品象征着质量好,安全放心。其实澳洲也有一些不良商家在搞假冒产品,前不久就有报道说,一些商家从欧洲进口牛肉,然后在澳洲加工,为的就是贴一个Product of Australia的标签。这在中国是没法想象的,因为对中国人来说,中国制造就意味着垃圾产品,要是食品的话还不知道有没有毒,从来只有本国产品假冒伪劣成进口产品,没有进口产品假冒成中国产品的。

各种 Product of Australia:

我的初中同学们大多都结婚生子了。很多人在群里谈论的是,要让婴儿吃XX国XX牌的奶粉,XX国XX牌的鱼肝油。就没有人推荐吃国货的。我在澳洲这边的同学搞代购都赚翻了,一包奶粉价格可以翻上好几倍。而且因为给中国做代购的人太多了,导致澳洲超市的奶粉货架上一直空空如也,各个超市还不得不做限购,依然阻挡不了中国人民的代购热情。还好澳洲本地人不喝奶粉,只喝牛奶,否则我觉得他们就要像香港人那样骂中国大陆人了。

在一个食品安全这样最最基本的问题都没解决好的国家里,有能谈什么盛世呢?同样很多爱国人士真的爱国吗?看起来好像是,但只限于和自己利益无关的情况下。当爱国青年们成家立业,有了孩子之后,还会坚持让小孩喝国产奶粉吗?我觉得不会。

爱国并不在于轰轰烈烈,而在于点点滴滴。如果你对本国的产品没有一点自信的话,那又有什么自信觉得中国出于盛世之中呢?

本博客不再保证国内可访问

由于最近墙的动作比较大,很多Linode东京的节点都被墙了,而本人目前在澳洲读研,没有时间和精力来关注博客是否在国内可访问,所以国内可访问性暂时不能做保证。

特此通知

Objective-C小技巧之——如何将自定义类的对象作为字典的Key

Objective-C里面,要把自定义类产生的对象作为字典的Key不想Java那样自然而然,而是要做很多事情。

前年写过一篇文章,提到了如何把自定义类的对象变成字典的key。当时为了搞明白这个,连OPENSTEP的源码都看了。这种方法虽然正统,但是非常麻烦,不仅要实现NSCopying,而且要把equal和hash都写了,有点悲剧。但实际上有一个小技巧可以在不实现这些方法的情况下,将对象作为key,那就是用NSValue。

参考文章:

http://stackoverflow.com/questions/11532306/using-an-object-as-key-for-nsdictionary
http://nshipster.com/nsvalue/

示例代码:

关于编程语言的一些思考

这几年脚本语言一直在大行其道,Python,Ruby,JavaScript等语言几乎被用到了行业的各个方面。尤其是JavaScript,在Node.js之后几乎成了统御前后端的万能语言。

然而正是在这个大背景下,也有非常多反其道而行的大公司。比如Google对JavaScript似乎一直不满意,他们想了很多办法尽可能不去用JavaScript。一开始出现了GWT,目的是把Java编译成JavaScript,后来又出了一个Dart语言。Apple也在这个时间推出了Swift,比起Objective C的各种动态特性,Swift语言本身就显得静态了许多,也严格了许多。

我虽然从业只有2年多一点,但在这些时间里面有幸涉猎了各种语言,我个人认为,对于一般公司而言,比较适合那些有静态类型检查,语法也偏静态,并且是编译型的语言做开发。这是因为,虽然脚本型语言在初期开发时有快速灵活的优势,但维护成本却非常的高,而且代码腐烂的速度也会很快。可以说“灵活”这个词,在编程语言里面接近于“坑”的代名词。

我最不喜欢脚本型语言的一点是,没有静态类型检查。这会对后期维护产生非常多的问题。因为缺少静态类型检查,所以在函数声明时无法声明参数类型,也无法声明函数返回类型,比如:

当你看到这个函数的声明时,如果没有文档和注释,你无从得知这个函数能接收那些参数,它的返回值是什么样的。而如果有callback就更加悲剧,因为你更不知道callback里面能有什么入参。你唯一能做的就是阅读源码。

相比而言,有静态类型检查的语言就好多了。比如对等的Swift代码:

虽然它的声明看起来很啰嗦,但你可以不用看函数体,甚至不用看文档(如果函数命名、变量命名很好的话)就能猜出需要传哪些类型的参数,callback会有怎么样的入参,函数将返回什么etc.

正因脚本语言没有类型检查,不用写返回类型等等特性,导致编辑器和IDE很难做错误提示,并且这些语言多是解释型语言,没有编译器的编译检测,很多错误只能通过单元测试和运行过程中才能发现。

所以我认为,脚本类的语言,对于开发者的要求非常之高。不但需要尽可能地多写单元测试,还要很好地维护文档。这对一个拥有良好习惯的编程高手来说,并不是一个大问题。但软件开发很少有一个大牛从头到尾开发一个软件,更没有一个软件从头到尾都是由一个大牛维护的。软件工程里面最不可缺少的就是程序员之间的交流合作。而对于普通公司的普通团队来说,根本不可能保证不同时期的所有的team member都是习惯良好的一流高手。而人又是有惰性的,如果一件事情可以不做,则很多人会选择尽量不做。于是用脚本类语言开发的程序渐渐地会变成一个一个大坑——因为单元测试不是强制的,所以可以不写;因为程序没有文档也能运行,所以可以不写;因为不做类型判断程序也能运行,所以就不做判断。

所以在管理一般性的团队的时候,重要的并不是怎么调动大家的灵活性进行开发,而是怎样让事情变得规范化和可控。比如多数团队都会制定一个Code Style来强制规范组员代码的风格(但由于不遵守代码规范的代码也能运行,所以经常会碰到违反的情况,这时候就需要代码审查,但是审查人也会有偷懒的时候,所以Code Style很多时候也不能完全规范化代码)。而强制性最高的,莫过于拥有类型检查的编译器了——因为如果不能通过编译器的检查,代码根本就不能运行,所以大家只能老老实实地遵守编译器规范,比如该写接口的时候写接口,该用泛型的时候用泛型,函数传参类型等等就更不在话下了。因此,这种规范较多的语言,在整体水平一般的团队中,使用起来比较方便,也更容易“横向扩展(即用较低的价格雇佣一些不完美的程序员来堆砌代码)”。

最近的一些新语言发展趋势,也证明了那些拥有静态类型检测的语言将会是业界新宠。比如Go, Scala, Swift, Dart这样的语言,看起来都非常接近,他们有静态类型检测,也偏向于静态,又增加了类型推导,从而不像Java那样啰嗦,也加了函数式编程的特性,引入新的并发模型等等。而脚本类的语言,则在产品原型开发、小型脚本编写中比较方便,在大型项目中就显得有点坑了。

使用Python读取IPA中的软件信息

最近公司需要对所有的iOS工程进行持续集成,软件的编译、打包、内测版的ipa发布都需要自动化。在做ipa自动化发布时,需要拿到一个打包好的ipa之后提取出里面相关的信息,比如显示名称、版本号、bundle identifier等等。

其实要做到这一点是非常容易的,前提是你要对ipa包的结构有所了解。

ipa文件结构

首先,一个ipa其实就是一个zip文件改了后缀名。如果你把ipa的后缀改回.zip,那么你就能通过各种解压软件直接解压了。

解压后的目录结构是:

其中,Info.plist就是软件信息的所在。

但是别高兴得太早,这里的Info.plist并不是纯文本文件,而是一种被称为Binary Plist的东西。之前在做iOS开发时,工程里面用的plist几乎都是XML形式的纯文本文件,所以一开始我理所当然地认为这个也是纯文本文件,结果在这里卡了半天。在命令行运行man plist就能看到下面这段话:

The property list programming interface allows you to convert hierarchically structured combinations of these basic types to and from two formats: standard XML and an optimized, opaque binary format.

所以不能看到.plist结尾的文件就当文本文件处理。

Python中相应的library

在知道ipa的结构之后,就能知道需要用哪些Python library了,第一个是zipfile,这个库能够处理zip类型的文件,并且可以在不解压的情况下读取里面某个文件的内容。另外一个是plistlib。但是需要注意的是,在Python 3.4之前,这个库是不支持 Binary Plist 的解析的。所以如果使用 Python 2.7 的话,需要使用三方库biplist

下面使用 Python 3.4 为例进行解析。之所以选择 Python 3.x 是因为 2.x 版本在处理 Unicode 和非 Unicode 混用时非常麻烦。而 Python 3.x 起,所有的 string 都使用 Unicode 编码了,就跟 ObjC 中一样。

首先使用zipfile将ipa文件打开,zipfile中,namelist()方法能够列出里面包含的所有文件路径,并返回一个list。根据ipa的结构,我们要找的Info.plistPayload/软件名字.app/Info.plist,所以这里使用一个正则表达式找到这个文件路径。

ZipFile.read()这个方法,能够在不解压zip文件的情况下读取里面的内容,在Python 3.4中返回的是bytes类型,再使用plistlib.loads()载入。其中,loads()是 Python 3.4 才加入的新API,它接收一个bytes对象,将其解析成相应的Python对象。

这样,这个plist文件就变成了一个dict,从而可以取到里面的内容。

如果使用 Python 2.7, 则可以用biplist代替,这个库使用的是跟 Python 2.7 中plistlib相同的API。

如果不使用 Python,或者不想用这些类库,则可以通过一些外部程序来解决Binary Plist的读取问题。比如使用/usr/libexec/PlistBuddy或者是plutil,这些都是 OS X 自带的工具,通过它们,你可以读取Plist中的指定项,或者直接把plist变成JSON文件。

[译] Xcode 6的“矢量图支持”是如何工作的

翻译自这个答案

矢量图在Xcode 6中如何使用

  1. 以一倍图(@1x)大小将图片保存成PDF格式的矢量图
  2. Images.xcassets文件中新建一个Image Set
  3. attributes inspector中,将Type设为Vectors
  4. 将pdf文件拖拽到All, Universal
  5. 这样,你就可以通过名称(指images.xcassets对此图片起的名称)来指向这个图片,跟使用png图片一样。

矢量图在Xcode 6是如何工作的

“矢量图支持”在Xcode 6中可能跟大家想象的有所出入,因为很多人想到矢量图的时候,他们想到的是那些可以被扩大,缩小,但是还能显示得完好的图片。然而,Xcode 6对于iOS的矢量图支持并不完全,所以它的做法有点不同。

为了消除这种疑惑,在此说明矢量图系统的工作流程如下:

  • 它只是一个转换系统,它在 构建期 通过你的png文件生成@1x.png, @2x.png, @3x.png

比如,假定你有44×44大小的foo.pdf矢量图,在 构建期 ,它会生成如下的文件:

  1. foo@1x.png 44 x 44
  2. foo@2x.png 88 x 88
  3. foo@3x.png 132 x 132

这意味着:

  • 你不能更改图片的大小(指的是把一个44×44的图片塞进100×100的ImageView里)。它只有在44×44的大小中才看起来对。原因是(iOS)并没有实现矢量图的完全支持。这些矢量图的作用只是让你保存image assets的时候帮你节省时间。如果你已经用了某些工具,比如一个Photoshop脚本,这些pdf文件的作用只是对未来可能出现的情况的适配(比如如果苹果要求对iOS 9进行@4x图片的支持的时候),以及减少文件的管理。
  • 你必须将所有的矢量图保存为@1x大小的PDF格式。这样做才能保证UIImageView有正确的固有大小(intrinsic content size)

苹果这么做的原因是:

  • 能对老版本的iOS向下支持。
  • 运行时缩放矢量图是一个计算密集型的工作,但是通过上面这种方式,就不会对性能造成任何影响。