这就是为什么我不喜欢看中文翻译的技术文章
原文: 25 iOS App Performance Tips & Tricks
- Use ARC to Manage Memory
- Use a reuseIdentifier Where Appropriate
- Set Views as Opaque When Possible
- …
翻译版本: iOS应用性能调优的25个建议和技巧
- 用ARC管理内存
- 在正确的地方使用reuseIdentifier
- 尽可能使Views透明
- …
注意看第三条。
技术文看中文翻译版本确实省时省力,但是如果翻译质量不好或者出错的话就会被带到沟里。其实技术文章的词汇非常集中,同一技术点的单词翻来覆去就那几个,多看几篇文档就熟了。
PS:没有一点炫耀英文的意思,大学英语四级 426.
Jawbone UP 2 Review
1 月 31 日开始用已经有两个月,简单记一个评测。
优点:
- 心理暗示作用。为了完成每天的步行目标,真的很愿意去走,比如地铁换乘更乐于走楼梯而不是电梯,以满足自己报表上 100% 完成度的虚荣心。
- 腕带式设计确实很方便携带。除了洗澡,现在几乎是 24 小时不离身,不存在忘带的情况,完全忽视了它的存在。
- 智能闹钟很强大。在指定时间前 30 分钟内从轻度睡眠状态中把人叫醒,起床完全不痛苦,自然醒一般。
- 睡眠质量记录功能。何时上床何时入睡何时醒来,充分了解自己的睡眠。
- 可以互加好友然后激励自己锻炼,不过目前好友间的互动功能还很弱。
- 待机时间可达 7 天以上,极少中断工作。
缺点:
- 手动切换睡眠模式太不方便,很容易忘记,这个月就有三次忘了切换睡眠然后整个睡眠为 0。如果能根据平均上床时间提醒或自动切换睡眠模式可能会更好。
- 手动同步不太方便,好几次都是打开 App 要查看记录才发现还没同步。
- 模式切换的按钮容易坏掉,我这个现在按下的反馈力已经不如全新时干脆,耳机头保护帽容易丢。
- 腕带设计会有误差,推荐非利手佩戴,减少记录误差。
总的来说对 Jawbone UP 2 很满意,使用简单,静默无干扰。可穿戴式设备绝对是下一个爆发点,期待 UP 下一代。
离家的孩子
2013-03-25:
从来没有像这次一样讨厌自己离开家。
第一次送妹妹去学校,妹哭的一塌糊涂。在学校门口我使劲压着泪说学习要努力,妹含泪说不哭,加油。可等我送水杯到宿舍的时候,小姑娘再也没忍住,失控的哭声让我完全没法哄她,我只能抱着等她哭到累才停下。可以有很多理由解释为什么要离开家,可在那一刻,所有的借口都很无力。
你还要继续做一个离家的孩子吗?
iOS Crash Report Service Comparison
实验对比一下现有的 iOS Crash report 服务。包括 Google Analytics(GA), Crashlytics, TestFlight, HockeyApp/QuincyKit/HockeyKit, Crittercism, Bugsense, Flurry.
Google Analytics
- 手动或 CocoaPods 添加库,设置统计 ID,开启 trackUncaughtException,使用很简单。
- Crash 报表比较简陋,可以根据应用版本号、iOS 版本区分,然后根据 crash description 分类,堆栈描述信息比较少,只有 crash 部分栈信息。
- 通过 try-catch 可以有目的性的对 NSException、NSerror 进行捕捉。
- GA 2.0 仍在 beta,稳定性需要验证 。
- 免费。
GA 集成,可以少添加一个库,类似统计的方式做 crash report,crash 信息比较简单,适合简单使用。
Crashlytics
- 相较其他库手动添加或者用 CocoaPods 方式引入,Crashlytics 需要一个软件来集成,刚开始会比较不习惯。按流程走,选中项目,添加 Build Phase-Run Script,添加 framework,设置 APIKey,Done。
- 堆栈信息完善,crash 自动分类,然后作为一个 issue 列出,可以列出 crash 设备信息 (JailBroken, free space, free RAM,屏幕旋转方位,network type 等),这些信息对于 crash 筛选和原因查找会有很大帮助。
- issue 有 open/close 两个状态,方便解决统计。
- 支持 developer team。
- 被动收集,没有主动收集方式。
- crash 邮件报告,支持 Redmine 等第三方服务集成,方便 bug 提交管理。
- 被 Twitter 收购后企业版改为免费。
Crash 信息完善,分类清晰,适合对 crash report 要求比较高的场景使用。
TestFlight
- 手动或 pod 添加,打包上传到 TestFlight,获取 token。使用逻辑比较混乱,先上传 app 才能拿到 token。
- 支持应用分发,feedback,remote logs,Sessions,Checkpoint 等统计功能。
- 单纯 crash 的话使用还比较简单,不需要做特殊处理,其他功能需要针对处理。
- crash 发送好像有点问题,crash 了几次后服务器都没有收到,所以也没法看到 crash 统计。
- 支持 developer team。
- 免费。
看起来功能很多,但是都不够深度,crash report 功能不堪大用。
HockeyApp/QuincyKit/HockeyKit
- pod 添加 SDK,打包上传,获取 token;手动添加流程看起来非常麻烦。
- crash 自动分类,栈信息完整,会把关键信息提炼出来。crash status-Open/Resolved/Ignored。
- 支持应用分发、feedback。
- 支持和第三方 bug tracker 集成。
- HockeyKit、QuincyKit 是开源版本的 HockeyApp,均有客户端和服务端代码,QuincyKit 只有 crash report,HockeyKit 只有应用分发和更新。
- 手动上传 dSYM。
- HockeyApp 收费,免费试用一个月。
简单说就是 TestFlight 加强版,应用分发 + crash report.
Crittercism
- 使用简单,先在网站注册一个应用,获取 token,不需要上传 ipa 到网站。
- 按 crash 原因归类,堆栈信息完整,高亮标明主要信息。crash 报表清晰,可以很明确的查看 crash 历史,设备信息(RAM,iOS version,device,network 等)。
- 支持主动有目的性 exception 收集。
- 支持 crash status(unresolved,resolved,known)。
- 需要手动上传 dSYM,估计是为了 release 下使用。
- 支持 crash alarm,SMS、邮件接收,支持 Uservoice 服务集成。
- 支持 developer team。
- 居然还有一个 rate app alert 功能。。。
- 有免费套餐,专业版支持简单的应用统计。专业版每月活跃用户 100K (per 100k MAU),限制比较大。
- 初创公司,获得风投,和 Crashlytics 气质最像的一个。
功能强大的专业的 crash report 服务。
Bugsense
- 网站注册应用,获取 token。
- 客户端是用 PlCrashReporter 做 crash 收集。
- crash report 可以按 status/App version/OS version 过滤 (付费版)。
- 发送 crash report 的时候可以附带一些自定义数据。
- 有一个比较神奇的功能,Fix Notification,如果某个 crash 已经标记为 resolved 并且新版本已经上线,可以弹窗提醒用户该 crash 已经解决,引导用户去更新升级 (付费版)。
- crash 收集服务被墙。 There are cases where our servers are being blocked due to geographic restrictions (e.g. China).
- 应用应用使用统计,支持 Event 统计
- 免费版限制太多,基本不可用。
相对来说功能比较简单的 crash report 服务。
Flurry
- 网站注册应用,获取 application key。
- 做统计出身,所以 crash report 功能只能算是一个附属功能,crash log 非常简单。
- 免费。
统计服务附带 crash report,功能简单。
小结
- crash report 要求不高且在用 GA/Flurry 统计的话,直接用附带的。
- 需要更为专业详细的 crash report,Crashlytics/Crittercism 二选一。
- 需要应用分发的话上 HockeyApp。
个人倾向于 Crashlytics。原因:
- 内部测试应用分发都比较简单,可以用脚本+内部服务器搞定,比如这个 build.py。
- Crittercism 有 MAU 限制,付费升级到 Premium 也限制 100K MAU。
- Twitter 收了 Crashlytics 后很大方的把企业版免费,开发也在继续。
- 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。
- 查看当前 ipfw 规则:
sudo ipfw show
- port 80 to 8080 forward:
sudo ipfw add 100 fwd 127.0.0.1,8080 tcp from any to any 80 in
- 清除 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
工作
- iDev 深入,比如 runtime,自动化测试等,尝试一下 OS X 开发。
- 服务端开发学习。
- 学一门新语言,Lua/Go。
生活
- 学车考驾照。
- 健身锻炼。
Hidden Features in osx.plugin.zsh
oh-my-zsh 自带了很多插件,其中 osx.plugin.zsh 里有不少好东西。
cdf
快速在终端打开当前 Finder 所在目录。之前的方法是用 cdto,缺点是会另外开一个终端窗口;或者鼠标拖动目录到终端再 cd。cdf 就省力很多,也是这个插件最喜欢的一个命令。trash
替换 rm,文件被移动到废纸篓而不是真正删除,避免误操作。alias rm='trash'
pfd/pfs
打印当前 Finder 所在目录,一般配合 cdf 来用。pushdf
pushd 寄存当前 Finder 所在目录。quick-look
调用 QuickLook 查看文件,配合 QLMarkdown.qlgenerator 快速预览 Markdown 文件。man-preview
把 man 信息生成 pdf 然后用预览打开。tab/split_tab/vsplit_tab
新建、切割终端 tab,不太习惯切割终端,这个用的很少。