Jawbone UP 2 Review

1 月 31 日开始用已经有两个月,简单记一个评测。

优点:

  1. 心理暗示作用。为了完成每天的步行目标,真的很愿意去走,比如地铁换乘更乐于走楼梯而不是电梯,以满足自己报表上 100% 完成度的虚荣心。
  2. 腕带式设计确实很方便携带。除了洗澡,现在几乎是 24 小时不离身,不存在忘带的情况,完全忽视了它的存在。
  3. 智能闹钟很强大。在指定时间前 30 分钟内从轻度睡眠状态中把人叫醒,起床完全不痛苦,自然醒一般。
  4. 睡眠质量记录功能。何时上床何时入睡何时醒来,充分了解自己的睡眠。
  5. 可以互加好友然后激励自己锻炼,不过目前好友间的互动功能还很弱。
  6. 待机时间可达 7 天以上,极少中断工作。

缺点:

  1. 手动切换睡眠模式太不方便,很容易忘记,这个月就有三次忘了切换睡眠然后整个睡眠为 0。如果能根据平均上床时间提醒或自动切换睡眠模式可能会更好。
  2. 手动同步不太方便,好几次都是打开 App 要查看记录才发现还没同步。
  3. 模式切换的按钮容易坏掉,我这个现在按下的反馈力已经不如全新时干脆,耳机头保护帽容易丢。
  4. 腕带设计会有误差,推荐非利手佩戴,减少记录误差。

总的来说对 Jawbone UP 2 很满意,使用简单,静默无干扰。可穿戴式设备绝对是下一个爆发点,期待 UP 下一代。

离家的孩子

2013-03-25:

从来没有像这次一样讨厌自己离开家。

第一次送妹妹去学校,妹哭的一塌糊涂。在学校门口我使劲压着泪说学习要努力,妹含泪说不哭,加油。可等我送水杯到宿舍的时候,小姑娘再也没忍住,失控的哭声让我完全没法哄她,我只能抱着等她哭到累才停下。可以有很多理由解释为什么要离开家,可在那一刻,所有的借口都很无力。

你还要继续做一个离家的孩子吗?

R.I.P Google Reader

R.I.P Google Reader

iOS Crash Report Service Comparison

实验对比一下现有的 iOS Crash report 服务。包括 Google Analytics(GA), Crashlytics, TestFlight, HockeyApp/QuincyKit/HockeyKit, Crittercism, Bugsense, Flurry.

Google Analytics

  1. 手动或 CocoaPods 添加库,设置统计 ID,开启 trackUncaughtException,使用很简单。
  2. Crash 报表比较简陋,可以根据应用版本号、iOS 版本区分,然后根据 crash description 分类,堆栈描述信息比较少,只有 crash 部分栈信息。
  3. 通过 try-catch 可以有目的性的对 NSException、NSerror 进行捕捉。
  4. GA 2.0 仍在 beta,稳定性需要验证 。
  5. 免费。

GA 集成,可以少添加一个库,类似统计的方式做 crash report,crash 信息比较简单,适合简单使用。

Crashlytics

  1. 相较其他库手动添加或者用 CocoaPods 方式引入,Crashlytics 需要一个软件来集成,刚开始会比较不习惯。按流程走,选中项目,添加 Build Phase-Run Script,添加 framework,设置 APIKey,Done。
  2. 堆栈信息完善,crash 自动分类,然后作为一个 issue 列出,可以列出 crash 设备信息 (JailBroken, free space, free RAM,屏幕旋转方位,network type 等),这些信息对于 crash 筛选和原因查找会有很大帮助。
  3. issue 有 open/close 两个状态,方便解决统计。
  4. 支持 developer team。
  5. 被动收集,没有主动收集方式。
  6. crash 邮件报告,支持 Redmine 等第三方服务集成,方便 bug 提交管理。
  7. 被 Twitter 收购后企业版改为免费

Crash 信息完善,分类清晰,适合对 crash report 要求比较高的场景使用。

TestFlight

  1. 手动或 pod 添加,打包上传到 TestFlight,获取 token。使用逻辑比较混乱,先上传 app 才能拿到 token。
  2. 支持应用分发,feedback,remote logs,Sessions,Checkpoint 等统计功能。
  3. 单纯 crash 的话使用还比较简单,不需要做特殊处理,其他功能需要针对处理。
  4. crash 发送好像有点问题,crash 了几次后服务器都没有收到,所以也没法看到 crash 统计。
  5. 支持 developer team。
  6. 免费。

看起来功能很多,但是都不够深度,crash report 功能不堪大用。

HockeyApp/QuincyKit/HockeyKit

  1. pod 添加 SDK,打包上传,获取 token;手动添加流程看起来非常麻烦。
  2. crash 自动分类,栈信息完整,会把关键信息提炼出来。crash status-Open/Resolved/Ignored。
  3. 支持应用分发、feedback。
  4. 支持和第三方 bug tracker 集成。
  5. HockeyKit、QuincyKit 是开源版本的 HockeyApp,均有客户端和服务端代码,QuincyKit 只有 crash report,HockeyKit 只有应用分发和更新。
  6. 手动上传 dSYM。
  7. HockeyApp 收费,免费试用一个月。

简单说就是 TestFlight 加强版,应用分发 + crash report.

Crittercism

  1. 使用简单,先在网站注册一个应用,获取 token,不需要上传 ipa 到网站。
  2. 按 crash 原因归类,堆栈信息完整,高亮标明主要信息。crash 报表清晰,可以很明确的查看 crash 历史,设备信息(RAM,iOS version,device,network 等)。
  3. 支持主动有目的性 exception 收集。
  4. 支持 crash status(unresolved,resolved,known)。
  5. 需要手动上传 dSYM,估计是为了 release 下使用。
  6. 支持 crash alarm,SMS、邮件接收,支持 Uservoice 服务集成。
  7. 支持 developer team。
  8. 居然还有一个 rate app alert 功能。。。
  9. 有免费套餐,专业版支持简单的应用统计。专业版每月活跃用户 100K (per 100k MAU),限制比较大。
  10. 初创公司,获得风投,和 Crashlytics 气质最像的一个。

功能强大的专业的 crash report 服务。

Bugsense

  1. 网站注册应用,获取 token。
  2. 客户端是用 PlCrashReporter 做 crash 收集。
  3. crash report 可以按 status/App version/OS version 过滤 (付费版)。
  4. 发送 crash report 的时候可以附带一些自定义数据。
  5. 有一个比较神奇的功能,Fix Notification,如果某个 crash 已经标记为 resolved 并且新版本已经上线,可以弹窗提醒用户该 crash 已经解决,引导用户去更新升级 (付费版)。
  6. crash 收集服务被墙。 There are cases where our servers are being blocked due to geographic restrictions (e.g. China).
  7. 应用应用使用统计,支持 Event 统计
  8. 免费版限制太多,基本不可用。

相对来说功能比较简单的 crash report 服务。

Flurry

  1. 网站注册应用,获取 application key。
  2. 做统计出身,所以 crash report 功能只能算是一个附属功能,crash log 非常简单。
  3. 免费。

统计服务附带 crash report,功能简单。

小结

  1. crash report 要求不高且在用 GA/Flurry 统计的话,直接用附带的。
  2. 需要更为专业详细的 crash report,Crashlytics/Crittercism 二选一。
  3. 需要应用分发的话上 HockeyApp。

个人倾向于 Crashlytics。原因:

  1. 内部测试应用分发都比较简单,可以用脚本+内部服务器搞定,比如这个 build.py
  2. Crittercism 有 MAU 限制,付费升级到 Premium 也限制 100K MAU。
  3. Twitter 收了 Crashlytics 后很大方的把企业版免费,开发也在继续。
  4. Crashlytics 的网站设计更喜欢一些。

NSDateFormatter 返回一年前时间

NSDateFormatter 的一个小陷阱:

NSString *ds = @"2013-03-01 23:55:56";
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];
NSDate *date1 = [formatter dateFromString:ds];
NSLog(@"date1: %@", date1); //date1: 2012-03-01 15:55:56 +0000
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate *date2 = [formatter dateFromString:ds];
NSLog(@"date2: %@", date2); //date2: 2013-03-01 15:55:56 +0000

格式化以后是一年前的一个时间点,yyyy 指代的就是常规意义上的年,而 YYYY 是 Week of Year, 具体解释参考 Wikipedia: ISO_week_date

iOS 开发文档有相关提示:

It uses yyyy to specify the year component. A common mistake is to use YYYY. yyyy specifies the calendar year whereas YYYY specifies the year (of “Week of Year”), used in the ISO year-week calendar. In most cases, yyyy and YYYY yield the same number, however they may be different. Typically you should use the calendar year.

NSMutableAttributedString Notes

Core Text 针对文本段落支持的样式属性:

typedef CF_ENUM(uint32_t, CTParagraphStyleSpecifier) {
    kCTParagraphStyleSpecifierAlignment = 0,
    kCTParagraphStyleSpecifierFirstLineHeadIndent = 1,
    kCTParagraphStyleSpecifierHeadIndent = 2,
    kCTParagraphStyleSpecifierTailIndent = 3,
    kCTParagraphStyleSpecifierTabStops = 4,
    kCTParagraphStyleSpecifierDefaultTabInterval = 5,
    kCTParagraphStyleSpecifierLineBreakMode = 6,
    kCTParagraphStyleSpecifierLineHeightMultiple = 7,
    kCTParagraphStyleSpecifierMaximumLineHeight = 8,
    kCTParagraphStyleSpecifierMinimumLineHeight = 9,
    kCTParagraphStyleSpecifierLineSpacing = 10, /* deprecated */
    kCTParagraphStyleSpecifierParagraphSpacing = 11,
    kCTParagraphStyleSpecifierParagraphSpacingBefore = 12,
    kCTParagraphStyleSpecifierBaseWritingDirection = 13,
    kCTParagraphStyleSpecifierMaximumLineSpacing = 14,
    kCTParagraphStyleSpecifierMinimumLineSpacing = 15,
    kCTParagraphStyleSpecifierLineSpacingAdjustment = 16,
    kCTParagraphStyleSpecifierLineBoundsOptions = 17,

    kCTParagraphStyleSpecifierCount
};

使用方法:新建一个样式 CTParagraphStyleSetting,设置样式属性和相关值,然后添加到 NSAttributedString.

NSMutableAttributedString *attriStr = [[NSMutableAttributedString alloc] initWithString:string];
// 样式1: 两端对齐
CTTextAlignment alignment = kCTJustifiedTextAlignment;
CTParagraphStyleSetting alignmentStyle;
alignmentStyle.spec = kCTParagraphStyleSpecifierAlignment;//对齐属性
alignmentStyle.valueSize = sizeof(alignment);
alignmentStyle.value = &alignment;

// 样式2:行间距
CGFloat lineSpaceMax = 4.0f;
CTParagraphStyleSetting lineSpaceStyleMax;
lineSpaceStyleMax.spec = kCTParagraphStyleSpecifierMaximumLineSpacing;//最大行间距属性
lineSpaceStyleMax.valueSize = sizeof(lineSpaceMax);
lineSpaceStyleMax.value = &lineSpaceMax;

CGFloat lineSpaceMin = 1.0f;
CTParagraphStyleSetting lineSpaceStyleMin;
lineSpaceStyleMin.spec = kCTParagraphStyleSpecifierMinimumLineSpacing;//最小行间距属性
lineSpaceStyleMin.valueSize = sizeof(lineSpaceMin);
lineSpaceStyleMin.value = &lineSpaceMin;

CGFloat lineSpaceAdjust = 2.0f;
CTParagraphStyleSetting lineSpaceStyleAdjust;
lineSpaceStyleAdjust.spec = kCTParagraphStyleSpecifierLineSpacingAdjustment;
lineSpaceStyleAdjust.valueSize = sizeof(lineSpaceAdjust);
lineSpaceStyleAdjust.value = &lineSpaceAdjust;

// 样式3:最大行高
CGFloat lineHeightMax = 18.0f;
CTParagraphStyleSetting lineHeightMaxStyle;
lineHeightMaxStyle.spec = kCTParagraphStyleSpecifierMaximumLineHeight;//最大行高属性
lineHeightMaxStyle.valueSize = sizeof(lineHeightMax);
lineHeightMaxStyle.value = &lineHeightMax;

// 样式数组
CTParagraphStyleSetting settings[]={
    alignmentStyle, lineSpaceStyleMax, lineSpaceStyleMin, lineSpaceStyleAdjust, lineHeightMaxStyle
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, 5);
[attriStr addAttribute:(id)kCTParagraphStyleAttributeName
                 value:(__bridge id)paragraphStyle
                 range:NSMakeRange(0, [attriStr length])];
CFRelease(paragraphStyle);

// Emoji、中文、英文混排
NSDictionary *fontAttributes = @{
    (id)kCTFontFamilyNameAttribute  : @"Helvetica",
    (id)kCTFontCascadeListAttribute : @[
        (__bridge id)CTFontDescriptorCreateWithNameAndSize(CFSTR("AppleColorEmoji"), 0),
        (__bridge id)CTFontDescriptorCreateWithNameAndSize(CFSTR("ZapfDingbatsITC"), 0),
    ]
};
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)(fontAttributes));
CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, FONT_SIZE, 0);

[attriStr addAttribute:(id)kCTFontAttributeName
                 value:(__bridge id)font
                 range:NSMakeRange(0, [attriStr length])];
CFRelease(font);

// 字体颜色
[attriStr addAttribute:(id)kCTForegroundColorAttributeName
                 value:(id)FONT_COLOR.CGColor
                 range:NSMakeRange(0, [attriStr length])];

OS X 下用 IPFW 作端口转发

用 ipfw 监听本地 80 端口然后转发到 8080 等端口,方便本地开发时调试操作。ipfw 是 OS X 自带的防火墙程序,类似 Linux 下的 iptables。

  1. 查看当前 ipfw 规则:

sudo ipfw show

  1. port 80 to 8080 forward:

sudo ipfw add 100 fwd 127.0.0.1,8080 tcp from any to any 80 in

  1. 清除 ipfw 规则

sudo ipfw flush

Done.

via Port Forwarding (80 to 8080 for Tomcat) Using IPFW on Mac OSX

[self review:2012];

2012

2012 年度个人总结。先对一下去年计划:

工作

加强 iOS 开发,尤其是新技术的学习和整理,比如 ARC。

Done.

尝试 iPad 开发,这可不是简单的界面放大,整个用户交互都需要重新学习理解。

Done.

服务端开发,至少是应用级别要跟上,已经落户一步,不能拖太久。

FAIL

继续加强产品能力,尤其是整体把握。

Fail.

生活

完婚。

DONE.

给家里更多的帮助,让爸妈轻松一点。

Done.

希望今年能有一次旅游,厦门?

Done. 上半年的张家界+年底的版纳旅游。

每项按十五分的话刚刚过 70,及格分。


工作

今年在工作上做了一些侧重,尤其是下半年,更多偏向于技术开发。自己在产品、交互等方面并不特别擅长,可能偶尔会有一些灵感飞过,但整体上的把控力还很欠缺,细节不够严谨。所以从下半年开始,更多的重心放在技术上,code review,产品代码质量把控上。我不知道这种侧重好还是不好,对自己以后的职业走向是利还是弊,暂时来说我想先把技术能力再提高一下。团队也有很多参与产品的机会,所以产品方面还可以继续锻炼。

侧重技术并不是自己技术有多牛,相反,越做越发现自己还有太多太多不懂的东西。有不懂才会有收获,今年工作上最多的收获来自 iOS 开发有了一些新的技术学习和积累。iOS 开发是一个进化非常快的领域,保持积极的学习状态很重要。ARC、GCD、Blocks、Core Data、AFNetworking、CocoaPods、UITableView 以及 App 的性能优化等等,这些技术点都遇到了很多坑,交了学费,但是对自己的技术成长很有好处。问题的解决和技术学习都留了一些笔记,好记性不如烂笔头。

相较于客户端开发有了一些积累,服务端开发今年没有任何进步,这一点很失望。主要原因是自己太懒散,缺乏动手去做,没有实践就不能发现问题,没有问题连学费都没得交,怎么可能有进步。所以新的一年里服务端开发是自己要着重加强的,不管什么一定要动手去做。

生活

我们结婚了,这就是今年的最大的成就。罗列好需要做的工作,然后两个人一起努力各个击破,这种感觉很不错,不啃老,我们做到了。爸妈牵挂我们在外的漂泊,我们更心疼他们在家的辛苦。让爸妈的压力不那么大是我们最想要的,为此我们会尽自己最大的努力去分担。

我还在坚持着,努力着,在外漂泊的不安定感却一直都在。这种感觉从 12 岁出去念初中开始,只有宿舍没有家。还好,我们现在两个人在一起,我不坚强的时候有你,你不坚强的时候有我,该哭的时候哭了,该笑的时候笑了。

2013

工作

  1. iDev 深入,比如 runtime,自动化测试等,尝试一下 OS X 开发。
  2. 服务端开发学习。
  3. 学一门新语言,Lua/Go。

生活

  1. 学车考驾照。
  2. 健身锻炼。

Hidden Features in osx.plugin.zsh

oh-my-zsh 自带了很多插件,其中 osx.plugin.zsh 里有不少好东西。

  1. cdf 快速在终端打开当前 Finder 所在目录。之前的方法是用 cdto,缺点是会另外开一个终端窗口;或者鼠标拖动目录到终端再 cd。cdf 就省力很多,也是这个插件最喜欢的一个命令。
  2. trash 替换 rm,文件被移动到废纸篓而不是真正删除,避免误操作。alias rm='trash'
  3. pfd/pfs 打印当前 Finder 所在目录,一般配合 cdf 来用。
  4. pushdf pushd 寄存当前 Finder 所在目录。
  5. quick-look 调用 QuickLook 查看文件,配合 QLMarkdown.qlgenerator 快速预览 Markdown 文件。
  6. man-preview 把 man 信息生成 pdf 然后用预览打开。
  7. tab/split_tab/vsplit_tab 新建、切割终端 tab,不太习惯切割终端,这个用的很少。

Setup Octopress from existing repo

从已有的 Octopress repository 重新配置 GitHub Pages 托管博客,比如换了电脑却没有备份原来的设置。要求 source 分支已 push。

git clone [email protected]:fannheyward/fannheyward.github.com.git blog
cd blog
git checkout --track origin/source
# setup ruby with rbevn or rvm
gem install bundler
bundle install
rake gen_deploy # in order to create _deploy dir
# setup blog branch
cd _deploy/
git init
git add .
git commit -m "new setup."
git remote add origin [email protected]:fannheyward/fannheyward.github.com.git
cd ..
rake deploy

其实就是做了一系列的 git 操作,设置 repo,branch 等,熟悉 git 很容易搞定。