朋友圈那些谣言

2014-8-10 评论(1) 分类:随记

好像不止一次因为朋友圈那些“健康谣言”跟人争论了,我个人特反感这些谣言,特别希望朋友能不要轻信它们转发它们,但太困难了。

为什么这些健康谣言有那么多人转发那么多人信?因为:

1.它们看起来是“善意”的,是对自己好的,多是保养保健,生活注意事项,食品安全等方面,朋友看了觉得咦还有点道理,转发给我的朋友们看看,让他们也健康生活。完全没问题啊,我是善意的,就算它最后被大众公认是谣言,也无伤大雅,善意是挡箭牌,但实际上善意造成恶果的例子在这社会上比比皆是。

2.思考和求证太麻烦。“我给大家分享一篇健康的文章,还要先去思考和考究这篇文章的真假?”我想说是的,应该这么做,而且现在的考究不过就是动动手指搜索一下,但大部分人无法接受这样的做法,太麻烦了。大家对这些文章采取的态度是宁可信其有,懒得思考它的真假,信了没什么成本,不信万一是真的呢?这可能算是现代迷信,古代迷信是:有个穿着道袍的人跟你说,不把村里一个处女扔河里,龙王就不会让天下雨,大家就信了。现在迷信是,一位名字写着“健康养生专家”的人跟你说,黄瓜是喂了避孕药的,微波炉加热食物会致癌,大家就信了。

另外看到两个观点:

1.不可能平白无故的有人这么说,肯定还是有一定的依据才会有这些言论。
深谙哲学中的“存在即合理”,其实我也不太理解为什么会有这么多人杜撰这些谣言,可能是一些平台为了流量雇写手写这些极易被转发的文章,用于积累用户和賺取广告费。

2.你说谣言文章是错的,你怎么证明辟谣文章是对的?
这问题让我有当年方舟子置疑韩寒代笔和身高的感觉,有点无奈,你说辟谣文章不信,我去找相关科学论文,你说相关科学论文也不信,那我就没办法了,现代还没有绝对的真理,还未能绝对解释任意事物的原理,对人体更是一知半解,很多健康问题无法用绝对的科学实验证明。其实很多谣言用常识就可以判断,根本无需辟谣文章,例如晚上几点肝排毒,几点胆排毒,且不说排毒这个概念,时间是人定的,中国共用北京时间,不同地区有时差,人体怎么知道这个时差?但即使这样要说服对方也是不容易。

这些谣言在朋友圈已经流行了一年了,在朋友圈这样封闭的空间里传谣容易辟谣难,而微信对它采取的是放任的态度,没看到任何实质性的改进,不知这是为什么,最后竟然是国家出手干预整治,真是一件神奇的事情。

推荐阅读:朋友圈那种高质量的科学谣言是如何被编造出来的?

iOS APP可执行文件的组成

2014-7-30 评论(30) 分类:技术文章 Tags:

iOS APP编译后,除了一些资源文件,剩下的就是一个可执行文件,有时候项目大了,引入的库多了,可执行文件很大,想知道这个可执行文件的构成是怎样,里面的内容都是些什么,哪些库占用空间较高,可以用以下方法勘察:

1.XCode开启编译选项Write Link Map File
XCode -> Project -> Build Settings -> 搜map -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置
linkmap

2.编译后,到编译目录里找到该txt文件,文件名和路径就是上述的Path to Link Map File
位于~/Library/Developer/Xcode/DerivedData/XXX-eumsvrzbvgfofvbfsoqokmjprvuh/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/

这个LinkMap里展示了整个可执行文件的全貌,列出了编译后的每一个.o目标文件的信息(包括静态链接库.a里的),以及每一个目标文件的代码段,数据段存储详情。

1

以伊书项目为例,在LinkMap里首先列出来的是目标文件列表:

# Object files:
[ 0] linker synthesized
[ 1] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/usr/lib/crt1.o
[ 2] /Users/bang/Library/Developer/Xcode/DerivedData/yishu-eyzgphknrrzpevagadjtwpzzeqag/Build/Intermediates/yishu.build/Debug-iphonesimulator/yishu.build/Objects-normal/i386/TKPFileInfo.o
...
[280] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANJob.o)
[281] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANWorker.o)
[282] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(MobClick.o)
[283] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANLaunch.o)
...

前面中括号里的是这个文件的编号,后面会用到,像项目里引用到静态链接库libMobClickLibrary.a里的目标文件都会在这里列出来。

2

接着是一个段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)

# Sections:
# Address   Size     Segment   Section
0x00002740 0x00273890 __TEXT __text
0x00275FD0 0x00000ADA __TEXT __symbol_stub
0x00276AAC 0x00001222 __TEXT __stub_helper
0x00277CCE 0x00019D9E __TEXT __objc_methname
0x00291A70 0x00012847 __TEXT __cstring
0x002A42B7 0x00001FC1 __TEXT __objc_classname
0x002A6278 0x000046A7 __TEXT __objc_methtype
0x002AA920 0x000061CE __TEXT __ustring
0x002B0AF0 0x00000764 __TEXT __const
0x002B1254 0x000028B8 __TEXT __gcc_except_tab
0x002B3B0C 0x00004EBC __TEXT __unwind_info
0x002B89C8 0x0003662C __TEXT __eh_frame
0x002EF000 0x00000014 __DATA __program_vars
0x002EF014 0x00000284 __DATA __nl_symbol_ptr
0x002EF298 0x0000073C __DATA __la_symbol_ptr
0x002EF9E0 0x000030A4 __DATA __const
0x002F2A84 0x00000590 __DATA __objc_classlist
0x002F3014 0x0000000C __DATA __objc_nlclslist
0x002F3020 0x0000006C __DATA __objc_catlist
0x002F308C 0x000000D8 __DATA __objc_protolist
0x002F3164 0x00000008 __DATA __objc_imageinfo
0x002F3170 0x0002BC80 __DATA __objc_const
0x0031EDF0 0x00003A30 __DATA __objc_selrefs
0x00322820 0x00000014 __DATA __objc_protorefs
0x00322834 0x000006B8 __DATA __objc_classrefs
0x00322EEC 0x00000394 __DATA __objc_superrefs
0x00323280 0x000037C8 __DATA __objc_data
0x00326A48 0x000096D0 __DATA __cfstring
0x00330118 0x00001424 __DATA __objc_ivar
0x00331540 0x00006080 __DATA __data
0x003375C0 0x0000001C __DATA __common
0x003375E0 0x000018E8 __DATA __bss

首列是数据在文件的偏移位置,第二列是这一段占用大小,第三列是段类型,代码段和数据段,第四列是段名称。

每一行的数据都紧跟在上一行后面,如第二行__symbol_stub的地址0x00275FD0就是第一行__text的地址0x00002740加上大小0x00273890,整个可执行文件大致数据分布就是这样。

这里可以清楚看到各种类型的数据在最终可执行文件里占的比例,例如__text表示编译后的程序执行语句,__data表示已初始化的全局变量和局部静态变量,__bss表示未初始化的全局变量和局部静态变量,__cstring表示代码里的字符串常量,等等。

3

接着就是按上表顺序,列出具体的按每个文件列出每个对应字段的位置和占用空间

# Address Size File Name
0x00002740 0x0000003E [ 1] start
0x00002780 0x00000400 [ 2] +[TKPFileInfo parseWithDictionary:]
0x00002B80 0x00000030 [ 2] -[TKPFileInfo fileID]
...

同样首列是数据在文件的偏移地址,第二列是占用大小,第三列是所属文件序号,对应上述Object files列表,最后是名字。

例如第二行代表了文件序号为2(反查上面就是TKPFileInfo.o)的parseWithDictionary方法占用了1000byte大小。

使用

这个文件可以让你了解整个APP编译后的情况,也许从中可以发现一些异常,还可以用这个文件计算静态链接库在项目里占的大小,有时候我们在项目里链了很多第三方库,导致APP体积变大很多,我们想确切知道每个库占用了多大空间,可以给我们优化提供方向。LinkMap里有了每个目标文件每个方法每个数据的占用大小数据,所以只要写个脚本,就可以统计出每个.o最后的大小,属于一个.a静态链接库的.o加起来,就是这个库在APP里占用的空间大小。

写了个nodejs版统计程序可供使用:https://gist.github.com/bang590/8f3e9704f1c2661836cd

设计符合直觉的用户界面(WWDC2014 #211)

2014-7-1 评论(1) 分类:互联网

看了WWDC2014 Session 211 – Designing Intuitive User Experiences,觉得挺不错,写下笔记。

用笔写字这个技能是我们从小就开始一直锻炼的,刚开始我们写得乱七八糟,后来慢慢变好,在这个训练过程中,书写这个技能已经变成肌肉记忆,在书写的过程中完全不用去思考,不会去注意到是怎样书写,怎样握笔,用了什么笔什么纸,一切都在无意识中完成,书写这一行为已经成为直觉。熟能生巧,这就是直觉的来源。

直觉是训练出来的,专业技能的直觉普通人是没有的,飞机几百个按钮飞行员可以不假思索找到需要的按钮,普通人不行,因为你可能有着别人没有的直觉,所以若纯粹按照你自己的想法做产品,可能会让部分用户用起来感到沮丧,你需要站在用户的角度和情景去设计,了解他们知道什么,不知道什么,直觉是什么。(按国内的说法就是让自己进入小白模式)

为什么产品设计要符合用户直觉?因为这样用户用你的APP不用学习,不用思考,可以随心所欲,用起来很爽,就会去给好评,就会有更多人使用。

设计符合直觉的产品,有5个特点。

5.平台共性 (Platform Savvy)

世上有数不清的各种笔,但你知道他们都可以用于书写,你会用同样的握笔方式,书写方式使用它们,这是笔的平台共性,对一个个体的认识可以延伸到同一平台上其他相似物的认识。

iOS平台上有很多这样的共性,例如在iMail列表里,向左滑动会出现删除按钮,一旦你知道了这个操作,在iOS上其他APP的列表里,你想删除其中一项时你会尝试左滑,预期中它是应该会出现一个删除按钮的,如果没有出现,用户会觉得很沮丧,出现了会觉得很爽很流畅。如果你在一个平台上做的东西不符合这个平台的共性,用户就会觉得你这个APP很奇怪。

建议用iOS平台约定成俗的东西,例如返回按钮位置,按钮点击位置,图标大小,图片缩放等等,这样用户没有学习成本。如果你想打破这些规则,做标新立异的APP,那你需要通过各种途径告诉用户。

4.易于导航 (Easy to Navigate)

1.告诉你现在你处于APP的什么位置。Tab Bar标签/Navigation Bar标题都是干这事儿的。
2.告诉你还能去哪里。用tab bar的优势是把你能去的地方全列出来了。
3.告诉你去到那个地方会有什么东西。把一堆功能分成清晰的几类,让人能从分类中就大概知道那里有什么。

逐级前进

在用户真正需要一个功能之前,不要把这个功能展示给他。可以通过加深分类层次做到这点。虽然这会增加用户选择点击次数,但如果分类清晰,用户不用思索就可以找到他想要的功能,这是更好的体验。

可预期

不要轻易改变用户已经习惯的东西,有重新学习的成本。例如根据使用频率把导航列表的各选项调上调下,用户体验是很糟糕的。

让选择中状态清晰可分辨

Tab Bar的选中状态应该做成什么样?若是仅仅换个颜色,并不够突出选中状态,iOS的做法是把图标填充满,再换显眼的颜色。做选中状态的原则是,要让图标不换颜色时,用户都能隐约分辨这是选中状态。

连贯体验

iOS里相册点击一张图片,图片是从原来的位置放大的,让你确认点击的就是那张照片,返回也是,从大图缩回小图,让你知道这张图片在你缩略图列表的位置。若点击图片然后从右滑进一个大图则没有这种连贯的体验。

提供暗示

一些不易发现的交互方式要提供足够的提示。举例一个星辰APP,点击星辰本身不会去到详情页,要点旁边的(i)按钮才会,但点击星辰会让i按钮闪一下,表示可以点击这里。

少即是多

提供更少的选择,会让更多的用户去选择。若选择多了,用户不知怎么选,反而不选了。在苹果和梨中选一个很容易,在很多种水果中选一个很难。

抽屉式侧滑导航问题

优点:
1.不占用屏幕空间,省去了Tab Bar的空间
2.导航栏位置大,占满屏幕,甚至可以上下滑动

缺点:
1.无法解决上述“我在哪里”“我还能去哪里”这两个问题。导航被隐藏起来,不像Tab Bar一直在那里,让用户知道自己在哪里,还能去哪里。
2.切换点击次数多。Tab Bar只需要一次点击,瞬间去到目的地。侧滑需要点击汉堡图标,等动画做完,再选择目的地,再等动画做完,效率低。
3.要占用Navigation Bar一个按钮的位置。放左边会跟返回按钮冲突,一般解决方法是返回到最上级才出现这个导航图标,这是不好的体验。若导航按钮放右边,右边就不能放一些iOS上习惯放的按钮,如编辑,分享等,导致用户体验很怪。
4.侧滑导航把很多不常用的东西都放进去了,显得很杂乱。

3.清晰 (Clear)

语言

No big words 不要用晦涩的词,用所有人熟知的词。
Avoid jargon 不要用专业术语,除非你的用户群是专业人士。
Be descriptive 用语义清晰的词
Be succinct 简洁
Avoid truncation 避免句子太长被系统用省略号截断
Make text legible 选易读的字体,不要用乱七八糟的花哨字体。

图标

用大家都熟知含义的图标,例如信息图标和搜索图标。
用跟现实中大家熟知的物品形状相近的图标,如闹钟图标。另外别以为软盘是大家熟知的物品。
不要用一个图标代表不同意思,例如用搜索图标代表检查。
图标是不能被用户学习的。
图标是很小的,避免用复杂的图形,不然缩小了不易辨识。

动画

有时用动画可以清晰引导用户。如iOS相机的聚焦,首先聚焦框从大到小缩放到聚焦位置,让你注意力集中到这个聚焦框上,然后框闪了两下,提示你聚焦已完成,可以拍照了。
另一个例子是锁屏输入密码,若输错密码,四个密码原点会跳一下,可以让用户很直观地知道密码错了,而不用去读文字。

2.简单 (Simple)

不要做一个复杂的APP,繁杂的功能会把真正用户需要的功能掩盖隐藏在深处(不适用于中国)。不要给用户一些他们不需要的功能(或者是80%的用户不需要),过多的功能让用户分心,难以找到他们想要的东西。

1.专注 (Focused)

一个APP应该专注做一到两件事。小而美,打磨精品,为用户提供最好的解决方案,才能在120万APP中脱颖而出。

一个CoreText排版性能问题

2014-6-19 评论(2) 分类:技术文章 Tags:

伊书的阅读器是用CoreText排版的,在中文字体的选择上,自带的字体中只有黑体,后来发现日文字体Hiragino也是可以用于中文,而且显示效果比黑体好很多,于是选用了这个字体。但在CoreText排版中遇到个问题,用Hiragino字体排版速度非常慢,几乎是默认黑体的100倍,以下是用Instrument Time Profiler查看排版同一章内容的时间消耗:

Hiragino:

1

黑体:

2

时间都消耗在CTFramesetterCreateWithAttributedString里,CoreText又不开源,看不到内部实现,在TimeProfiler里再继续展开,到最后只能看到时间分别耗在两个TRunGlue::ComputeEndIndex()里,看不出什么头绪。

后来用Instrument Allocation查看APP的内存分配时,发现用Hiragino字体排版生成的CTRun非常多,一章内容有2万多个CTRun。按理说,若一篇文章字体格式全部一样,渲染到frame之前CTRun只会有一个,渲染到frame后一行一个CTRun,怎样都不应该出现这么多CTRun。

3

再测了用黑体排版生成的CTRun次数,这次符合预期,只有70多个:

4

可以猜测到用Hiragino字体速度慢就是因为生成了大量的CTRun,那为什么会出现这样的情况?断点进入渲染处,打出其中一行CTLine数据,如下:

5

发现一些文字自动被转为黑体了。原因是Hiragino只支持部分中文字体,而那些不支持的中文字体就要用黑体代替,又因为一篇文章里会交错出现很多Hiragino支持和不支持的字,于是无法用一个CTRun表示一整段文字,每个交错都用一个新的CTRun,导致CTRun非常多,排版处理逻辑变复杂,耗时变高。

终于找到问题的原因,但如果想用Hiragino字体显示中文,这个问题还是无解的。其实在iOS4.x里是没有这个问题的,估计当时CoreText还没有为字体问题拆分多个CTRun,在5.0才开始这样做。除了我使用Hiragino字体会遇到这个问题外,在中英混杂的文字里用英文字体也有这样的问题,因为英文字体不支持中文显示,还是会被拆分成很多个CTRun,用CoreText注重性能的人可以注意下这个问题。

两种增量更新方案

2014-6-5 评论(3) 分类:技术文章

在邮件/日历/SNS等客户端里,客户端数据要不断与服务端进行数据同步,在同步过程中,只拉取有修改的数据,称为增量更新,增量更新方案一般有两种,一是对比,二是日志。

对比

对比就是客户端请求服务端所有关键数据,跟本地已有的数据进行对比,筛选出增删改的数据进行更新。

用对比方法的好处是服务端什么都不用做,坏处是客户端逻辑复杂,耗网络流量。在这种方案里,数据的新增和删除很容易判断,根据客户端数据的id列表和服务端数据的id列表进行对比就行,若要判断哪个数据有修改则比较麻烦,需要取回数据进行对比,如果从服务端拉回所有对所有数据进行对比会很耗网络流量,有一个优化方式,就是对每个数据的修改进行标记。
以日历为例,一个日历可修改的字段很多,例如时间段,内容,邀请人等,全部拉回来对比不现实,对此可以在服务端给每个日历事件新增一个字段tag,表示这个日历事件的版本,服务端更新一个日历事件时会同时更新这个tag,客户端只需要取回每个id对应的tag,跟本地保存的tag对比,不一致表示这个日历事件已经更新,再去获取日历实体就完成更新了。

若服务端因为某些原因无法给每个数据保存一个版本标记,可以实时计算,在客户端和服务端约定一个算法,把所有可变参数拿出来,通过特定算法hash出一个值,对比这个hash值判断是否需要更新。

邮件协议IMAP,日历协议CalDAV就是用这种方式做增量更新,IMAP并没有做上述的优化,在判断邮件有没有更新时只能乖乖把所有数据请求回来对比,数据是XML,算是相当低效的协议。CalDAV给每个日历事件加了上述的tag,直接对比即可知道是否需要更新。

日志

日志指服务端记录数据的每一次增删改,用一个类似版本号的sync-key标记这次修改,客户端通过一个旧的sync-key向服务端请求,服务端返回这个sync-key与最新sync-key之间所有的修改给客户端,完成增量更新。

这个sync-key在服务端的实现上可以是时间,也可以是一个自增的id,sync-key之间有顺序关系就行。在一个数据集里,每次数据有更新,就新增一个sycn-key,并记录这次更新。图示这个过程:

Untitled Diagram

这个方案客户端逻辑很简单,但服务端负担较大,每次数据更新都要记录,客户端请求时需要查询给出相应的数据。这个方案在实际操作中还有两个问题:

一是时间长了服务端保存数据量过大。可以通过限制记录的条数解决,超过限制就删除最旧的记录。这样做会出现一个问题,若客户端带着在服务端已被删除的sync-key上来请求,该如何处理?一般做法是返回一个错误给客户端,让客户端重新拉取所有数据。

二是若客户端sync-key过旧,增量数据可能过大。客户端数据太老,有太多数据需要更新,若一次性返回所有增量数据,这个请求可能会很大,请求时间太长,成功率也会很低。解决方式是分多次请求,客户端和服务端可以约定一个字段作为阀值,服务端每次返回的增量数据量不超过这个阀值,若总数据超过这个阀值,则分多次请求,通过每次请求返回的sync-key定位下次请求该返回哪些数据。例如客户端sync-key是100,服务端最新sync-key是1000,阀值是50,客户端第一次带sync-key=100请求,服务端第一次返回sycn-key 100-150这一段增量数据,并返回sync-key=150,并有一个值告诉客户端这个sync-key还不是最新,客户端再带上sync-key=150请求,以此类推,直到sync-key=1000。

微软的Exchange/ActiveSync就是用这种方式实现增量更新,ActiveSync还用WBXML压缩了数据,更适用于移动端。此外日历协议CalDAV的也有一个扩展协议RFC6578使用这种方式。ActiveSync和CalDAV扩展协议都有分多次请求增量数据的策略。

————

对于Timeline式的数据,增量更新方式多是以上两种,或者这两种的变体,可以根据业务特性修改或简化其中的逻辑,例如对于微博Timeline,它可以不考虑微博的修改,不考虑同步评论转发数的变化,不考虑同步删除的微博,并且每一条微博都有一个递增的id,那它的增量更新逻辑就很简单,只需要把客户端最新一条微博的id作为since_id传到服务端,返回比这个id更新的微博就行了,这里微博id相当于日志方式的sync-key,算是对日志方式的一种简化。

在创业公司的体会

2014-5-23 评论(12) 分类:懒得分类

在一个创业公司做了一年半,做得不成功,但这次经历还是不错的,写一些体会。

人员配备

公司的人员配备中,技术人员占比大,拥有的资源都是技术资源,很不平衡,我们做互联网产品,服装垂直搜索引擎和比价工具,产品/设计/运营都很重要,但我们没有一个产品经理,设计只有一个实习生,运营也只有一个经验不太足的。产品经理通常是创始人充当这个角色,但如果做了一长段时间后发现不合适,是应该找人辅助的。

此外我觉得对一个正常的互联网创业公司,技术/产品/运营都应该有相应的负责人,这个负责人应该是联合创始人,至少不能是打工者角色,才能正常运转起来。但因为公司的一些特殊原因,导致我们真正做事情的人里除了创始人外其他都逐渐被视为打工者,这是很不健康的机制,整个团队的积极性大打折扣,会导致很多问题的出现,这时候如果产品没有快速增长,问题就会爆发了。一个团队在非上升期是很难带的,没有人心稳定的核心团队很容易恶性循环。

可以说人员配备方面的问题是公司不成功的根本原因,其他的现象,像产品做不好,方向不明朗等问题都是由人员配备问题引起的,其他问题也就不多说了。

技术环境

创业公司都会倾向于用最快的速度把产品搭建起来,在产品没有快速成长的时候,没什么机会碰到性能瓶颈/应用优化之类难以解决的问题,也就得不到深入学习技术的机会,能得到的只有从前端到后端各方面都做一遍的范围较广的技术经验,但还是那个问题,这些技术的深浅取决于这个产品规模有多大,一个很少人用的产品只需要很简单的技术方案,随着用户量增加,需求的增加,所要解决的问题才逐渐增多,技术方案才会逐渐复杂,解决问题的价值才会显露出来。所以在创业团队,个人技术积累取决于产品的发展情况,否则只能靠自己平时业余的学习,如看看开源代码,但这种没有需求驱动的学习很困难,因为你没有遇到问题,看到的都是别人提供的解决方案,很难体会到他们这个解决方案背后的来源,也很难有一个明确的目标去学习,而且还需要很强的技术热情和自律才行。所以若是追求技术提升,还是大公司环境会更好点。

远程办公

因为初期人员很少,没有租办公地点,都在家工作,体验了一回远程办公,初期加上后期因办公室问题断断续续,在家办公有超过半年时间,总体感受是:短期很爽,长期不行。

公司

对公司来说,要实施远程办公,需要有很强的管理能力。很强的管理能力不是指把人管得死死的,装个摄像头监视你在家有没有工作,而是能给出合适的方法制度,消除远程办公带来的消极因素,例如员工散漫,自律困难,沟通不便等,但我们没有尝试这些制度的定制,一是觉得远程办公只是暂时的,很快会找办公室,不用搞这些。二是大家自由惯了,不想出一些条框限制自己。三是我觉得团队一直有一种散漫,这里有各种其他因素。结果对公司来说,远程办公带来的结果是消极的。

个人

对个人来说,远程办公带来极大的自由,每天不用去挤公交地铁,浪费路上那么多时间,在家里舒舒服服上班,冬天不用出去承受西北风,下雨天不湿身,大夏天不用穿得正正规规,自由安排时间,早上自然醒,下午去打个球游个泳晚上再回来工作也没问题,听起来是挺爽的,但长期这样是有问题的。

首先是自律问题,在家工作对自律能力的要求真是很高,做一会工作累了很容易就往沙发躺,没有什么时间概念,睡觉也比平时多了,经常搞搞这搞搞那时间不知怎么就过去了,一个人在家太自由导致散漫,两个人在家太繁琐导致分心,所以很多自由职业者常找个咖啡厅之类的公共场所工作。自律问题是可以克服的,虽然比较难,需要做好时间管理,给自己定死时间,XX时间段内不准离开座位不准做工作以外的事,我觉得严格要求并长期实施形成习惯后可以搞定这个问题,我试过一段时间,有点效果,但还是没坚持住,没做好。

其次是孤独感。有时一个人在家里一整天一句话都没说,跟团队的沟通也是通过QQ,不像在公司可以跟人面对面交流,吃饭时聊个天什么的,可能有人喜欢这样的,但长期这样会挺让人受不了的,人是高度社会化动物,我算是比较普通外向的人,不跟人面对面交流是会有问题的。另外这样也缺乏团队一起并肩作战的感觉,长期对团队建设是有害的。

最后是对成长不利,平时缺少跟人面对面沟通的机会,也就少了从他人身上学习的机会,见识不增长,长久下去思维也会局限。

独立开发者

这次创业期间,有段时间不忙,我尝试做了更多的个人APP,体验了一回独立开发者的滋味。

以前我觉得我应该适合做独立开发者,我很喜欢个人开发一些产品,之前大家都在问“你的梦想是什么”的时候,我觉得我的梦想应该是做出自己喜欢的产品,有很多人喜欢用,又能为自己带来收入。做独立开发者很符合这个愿望,一直想尝试,在腾讯期间就算很忙,也抽时间做了“伊书”这个APP,我本来觉得如果做的这些产品能带来收入,我就可以专心做自己喜欢的这些产品,不用在公司工作,但事实不是这样。其实这段时间的尝试已经实现了这个梦想的一部分,我做了许多工具/效率类APP,主打AppStore外国市场,有一些能获得不错的反响,虽然推广上很艰难,但也得到了一些收入,足以养活自己,但是做久了后我碰到一些问题:

1.一个人的问题。
详见前面远程办公的描述。一个人做很缺乏跟其他人的交流,难以融入社会,不适合我的性格。

2.没有一个有很好发展前景的产品,没有一个好的方法维持增长。
在营销推广上我的主要手段就是与国外限免网站合作,没有多少有效的手段,也没有做成一款十分流行的APP,没有资源的积累,感到十分吃力。

3.技术/设计/产品这三者我都可以做,但三者都没法做到最好。
很容易陷入什么都会什么都不精的尴尬境地,虽然足以做出小的产品,但一旦涉及规模较大的产品,这三方面的能力就都不足了,导致能做的产品和事情很局限。

经过这次体验,我知道独立开发者的工作方式不是我喜欢的。我最想做的事仍然是做自己喜欢的产品,但一个人能做出的产品太局限,我想通过一个团队做更大更好的产品,目前没有能力做到,还是需要先锻炼好相关的能力。

C++单例实现的坑

2014-4-20 评论(9) 分类:技术文章

单例本来是个很简单的模式,实现上应该也是很简单,但C++单例的简单实现会有一些坑,来看看为了避免这些坑怎样一步步演化到boost库的实现方式。

方案一

class QMManager
{
public:
    static QMManager &instance()
    {
        static QMManager instance_;
        return instance_;
    }
}

这是最简单的版本,在单线程下(或者是C++0X下)是没任何问题的,但在多线程下就不行了,因为static QMManager instance_;这句话不是线程安全的。
在局部作用域下的静态变量在编译时,编译器会创建一个附加变量标识静态变量是否被初始化,会被编译器变成像下面这样(伪代码):

static QMManager &instance()
{
    static bool constructed = false;
    static uninitialized QMManager instance_;
    if (!constructed) {
        constructed = true;
        new(&s) QMManager; //construct it
    }
    return instance_;
}

这里有竞争条件,两个线程同时调用instance()时,一个线程运行到if语句进入后还没设constructed值,此时切换到另一线程,constructed值还是false,同样进入到if语句里初始化变量,两个线程都执行了这个单例类的初始化,就不再是单例了。

(更多…)

“教育问题的意外答案”笔记

2014-4-9 评论(2) 分类:随记

最近发现听电台可以把上下班在路上的时间都用起来,不仅地铁上可以听,走路也可以,不像看书只有在地铁稳定的环境下才能看,感觉很好,难怪最近冒出那么多电台应用,不过至今没发现很好用的,只是凑合着选一个用。电台APP里名气最大的好像是荔枝FM,但它挺差的,这货应该是营销推广做得好。

这几天一直在听“罗辑思维”,就是一个自媒体胖子在那里讲他对一些主题的观点,以及他在书上看到的观点。主题大部分是文科/历史/新媒体互联网相关。语言诙谐轻松,讲得挺不错。在文科历史领域上我就是一张白纸,所以听他讲的那些东西感觉增长不少见识,但有时讲的一些我稍微有所了解的领域会发现他的一些观点站不住脚,甚至材料的真实性都没有保证,为了论证自己的观点把例子都给举扭曲了,略偏激。但总体还是不错的,推荐听听。

这两天听的一期“教育问题的意外答案”,对里面的观点有些看法,记录下当笔记。括号外是罗胖讲的内容,括号里是个人看法。

——————————————————————————————————————————————

教育问题本质是社会竞争太激烈,需要教育制度分流出不同能力的人,导致教育都是为这个需求服务,而不是为培养人才服务。大学扩招只是转移军备竞争的场所,让大家从比拼谁上得了大学,变成比谁上得了一本,没有改变很多,而一些辅助分流的机制在发展中国家中国又行不通。数学的作用不是教育,不是提升人的能力,更不是为了实用,而是充当把人分流出来的工具,数学是难易程度跨越最大,又能有确定性答案的学科,适合当这样的角色。
(就分流人才这个目的来说其实现在教育效果还可以,对个体可能没那么公平,但整体是符合期望的。)

高考建立在复习上,不断重复的复习是扼杀兴趣最厉害的手段,所以孩子不可能对高考制度下的学习产生兴趣。而学习成绩取得的成果带来的激励都是外部激励,而不是自己感到满足的内部激励。经受高考的摧残后很多人在高考过后就对学习本身反感,终身都不再怎么学习。
(很多人都可以从外部激励中得到满足,获得第一名的荣誉会驱使自己对追求学习成绩有兴趣,从而对无聊的重复学习本身有兴趣,至少是不反感。而对于没有在成绩上获得激励的人,确实可能会导致他们厌恶书本上的学习,但书本外的学习应该还不至于厌恶。最后社会竞争胜出的人很多都是保持好奇心,持续学习,对学习和提升能力本身有兴趣的人。)

创造力是培养不出来的,人们只知道怎么扼杀创造力,还不知道怎么培养它,想培养创造力最好的方法就是放任,提供最好的环境,降低人们获取相关资源的成本,然后少管闲事放任他们自由发展,而现在什么创造力的培养方案课程都是相反的。
(其实现在大学已经有点这样的意思,大部分人都是在考试前一两周把整个学期的课程学完应付考试,其他时间都是放任自由,在这些自由的时间里可以用学校的资源、身边优秀同学的资源去完成自己想做的事,真的有创造力的人已经可以在这样的环境下做出成绩。所以之前有人说网络公开课发达以后,大学就没必要存在了,真是扯淡,大学最有价值的不是那些课程,而是一群人聚在一起在大学这个有资源的环境下自由生活四年,创造力就是在这样的环境下成长发挥的。)

计划生育导致家庭里的孩子没有第二条路走,没办法像以前那样供养一个人读书,其他人分流到其他路比如做生意,减少竞争。作为独生子再困难也只能进入教育体系,稳妥发展,没有选择没有后路,使竞争更激烈。
(我的理解应该是独生子女读书压力更大了,再没有兴趣再不想走这条路也不得不走,因为家庭里没有人代替你,不像多子家庭,大儿子不想读,去搞艺术音乐,还有二儿子小儿子读,对整个家庭影响不大,大家选择的路窄了,竞争压力更大,激烈程度更高了。)

以前各知识领域都是一座座金字塔,必须进入教育体系里从底下往上爬,要获得知识没有第二条路, 但互联网使这些知识扁平化,随手可获得知识,也把各种专家权威拉下台,现在各行业很多厉害的人并不出在教育体系里,教育金字塔可能因为互联网崩溃。
(罗胖典型的万能互联网理论,套在这里实在太牵强,他说的这些跟互联网没半毛钱关系,一直以来有书本的传播知识的获取并不只是教育体系一条路,而且实用性知识一直没有在教育体系里,而是分布在企业/作坊里。教育是一条稳妥的路,其他路当然也能走出辉煌,但相对来说概率太低,互联网只是提供了更多机会,但没有改变本质。)

到后面大家温饱问题解决,每个人都追求自己的个人实现,大量人闲置,大家互相娱乐互相服务,靠兴趣生活,教育会不求结果,教育问题就自然解决了。
(虚无缥缈的共产主义乌托邦……大家就算吃饭没问题,也会其他因素竞争,人会不断为了欲望、社会地位等因素奋斗,大家追求的目标层次更高了,但竞争没变,压力没变,社会还是追求结果,教育也一样。现代人里的一个普通中产拥有的东西已经比古代的皇帝多了,但还是会不断追求更多,在这个社会金字塔上爬坡,欲望驱使他们获得更高的社会地位,恐惧失去现有地位驱使他们不断努力。人们的物质生活会越来越好,但竞争会一直存在,在未来人口增大,社会发达会给更多的人提供机会,会导致更激烈的竞争。)

iPhone越狱的安全性

2014-4-5 评论(2) 分类:互联网

风险

理论上iPhone越狱没有安全性可言,所有安装在你手机的APP都以root权限运行,它们可以:

1.随意读取修改系统上任意文件,获取微信支付宝等APP的数据,上传到自己的服务器保存。

越狱后所有APP都有权限访问系统任意文件,系统上APP存放目录是固定的,也有配置文件定位指定APP的目录位置,可以直接获取到这些APP的数据库等敏感文件。很多APP的数据库是没有加密的,聊天记录/邮件/日记什么的随便看。支付宝数据库倒是加密了,而且貌似有些数据不是用sqlite存储,目前我不知道能取出什么信息,这类敏感APP应该都在安全性上下了功夫的。

2.删除系统文件,导致系统崩溃。

可以恶意删除手机上任何东西,目前没见过这样病毒式的APP,反正任何APP都有能力可以做到。

3.通过Cydia Substrate插入动态库,给APP注入程序。

Cydia Substrate给开发者提供了一个方便的代码注入框架,所有cydia上的iOS插件都依赖它,一般人用来开发iOS上的插件,但也可以用它给指定的APP注入程序。开发者可以做到写一段程序,绑定一个APP,在这个APP启动时这段程序可以同时运行,并侦听这个APP的事件,或者修改这个APP里一些函数。图谋不轨的人可以用它做什么……就靠想象力了。这里有给支付宝注入程序让手势解锁失效的例子

避免

以上安全风险都基于一个前提:安装了一个恶意APP或插件。所以只要自己注意不要下载到这些东西就不会有问题。越狱后不知名的APP就不要下了,特别是那些只有盗版市场有的APP。不知名的盗版市场最好也不好用,cydia的源不要随意添加,下载插件要谨慎。

另外实际上AppStore上的APP在越狱环境下也不是绝对安全的,因为苹果也检测不到这些APP有没有做以上那些不轨之事。对于在盗版市场下载知名APP,似乎跟在AppStore上下载没区别,目前没见到有安装包被注入程序,不像Android市场。

像支付宝这样敏感的APP数据是有加密的,就算获取了也没那么容易破解。所以也不用太担心在越狱机器上用它们不安全。

虽然越狱后的iPhone有这么高风险,比Android不安全得多,但因为市场环境好,没多少恶意APP出现,所以情况还是乐观的,了解清楚情况,只要稍微注意点就没问题,至今没听说有人因越狱了iPhone损失了什么。

题外话

因为越狱后APP可以修改任意文件,所以APP也可以修改自身的启动画面,这样就做到动态替换启动画面了。有些APP在显示了启动画面后会出现广告,其实可以考虑在越狱机器上直接用广告替换启动页面,不用白白等待看广告的时间,提升体验。前段时间自选股就是在启动后出现三四秒的广告,体验极差,后来被撤下了,若是启动屏就是广告,不至于体验差到要去掉。

XReminder

2014-3-29 评论(1) 分类:作品

XReminder

XReminder是一个iPhone上的提醒工具,解决了快速添加提醒、稍后提醒的需求。

功能

这个APP的需求源于我自己,iOS自带的提醒工具要设个提醒十分麻烦,首先要在列表上添加一个todo->进入详情->开启“在指定日期提醒我”->点击提醒时间->选择时间->如果是重复事件再点击重复选项进入选择,这才完成一个提醒。

所以我做了XReminder,简化添加提醒的步骤,并加入我需要的其他功能。XReminder添加提醒很简单,下拉列表->输入标题->选择时间->完成,其中时间有On, After, Every三种:

On:具体时间提醒,没啥好说
After:指定时间段后提醒,例如想设置一个小时后提醒的事件,选择After再选1 hour就完成了。
Every:重复提醒,可以设每天/每周/每月/每年重复提醒

还有一个重要的功能,就是提醒时间到时有稍后再提醒的功能。我比较健忘,我用原生的提醒APP时,提醒时间到手机响后我看了一眼,如果那件事不是立刻就可以完成的,十有八九接下来我就又把它忘了,因为接下来不会再有提醒了。例如下班要带XX东西回家,提醒的时候还没能走,走的时候就又忘了这回事。我需要在我设置的提醒时间跟现实对不上的时候可以方便地让它稍后再提醒一下。XReminder就实现了这个功能,可以选择XX分钟后再提醒一次。

另外如果是一件很重要的事,提醒一次不够,一定要你看到并确认知道了才取消提醒,可以在新建任务的时候就设成snooze任务,时间到后每隔一分钟就会响一次,直到你打开APP点击DONE。

交互

整个APP就是垂直方向的上下拉动,上方是新建任务,中间是主列表,下方是设置。

添加提醒时向下拖动可以改变提醒条的颜色,这个本来是难以发现的,但在第一次使用时做了引导,第一次添加提醒时会遮罩提醒这里可以往下拖动改变颜色,只有用户拖了改变了一次颜色,这个提醒才会消失。

列表上左右拖动会出现编辑和删除按钮。左右拖动这个操作比较难发现,需要加以引导,所以点击列表时列表的元素会左右抖动一下提醒用户这个色块左右是松动的,可以试试拖动。

每个提醒主要显示了标题和剩余时间,右下角小字显示具体的提醒时间,包括重复的提醒时间的清楚表述,例如”Every Monday 10:00”。

设计

设计上,整个APP都是色块设计,看起来比较轻盈(实际上是这样比较容易做,我不会设计纹理)。按钮点击、上下切换视图和左右滑动都有声音配合。键盘和日期选择还是iOS6的风格,没有换成iOS7是因为iOS7的日期选择太丑了,更重要的是它没法选择控件字体的颜色,还没想到好的方法融入iOS7。