Meet @fannheyward

an iOS developer, blog about Web, Apps and my life.

Vim 文本选择范围

Vim 文本选择时可以用 a i 指定选择范围。a 代表一个整体(block),i 代表 inner。比如:

vaw 包括单词和单词后的空格,viw 只选中单词。帮助 :help v_aw/v_iw.

vat – select a tag block, 包括 本身,vit – select inner tag,只选择 包起来的部分。帮助 :help v_at/v_it.

vab 选中包括 () 在内的文本,vib 不包括 () 自身,等同 va(/va), vi(/vi). 帮助 :help v_ab.

vaB 选中包括 {} 在内的文本,viB 不包括 {} 本身,等同 va{/va}, vi{/vi}, 帮助 :help v_aB. 类似还有 va[vi[ 等。

v 换为 d 是就变成了删除操作,删除范围同上。

Only When

Well you only need the light when it’s burning low
Only miss the sun when it starts to snow
Only know you love her when you let her go

Only know you’ve been high when you’re feeling low
Only hate the road when you’re missing home
Only know you love her when you let her go

Let Her Go

Moving

如果我提前离开北京,那么房子可能是最大的原因。并不是要买房,而是租房都不让人省心。我只是想踏踏实实的租房,但现实是,房东嫌麻烦一般都把房子交给中介,无良中介又很多,让人心烦。 2013-03-03

来北京后第一次搬家,离开住了将近四年的天通苑。

一直很抗拒搬家,因为找房子搬家是个麻烦事,而我自己不知道从什么时候养成了一个毛病:事情在开始时候过于关注困难的部分,放大了可能出现的问题。这样的结果就是前期过于悲观,继而可能会影响自己的心情。这次也是如此,找房子时候烦中介,收拾东西时嫌东西多又不舍得扔,搬家还得找车找人,连续几天心情都是忽好忽坏。

这个毛病的“好处”就是如果事情发展没有想象中的那么困难,那会非常有干劲,因为最坏的情况已经有了思想准备,后续的发展都可以轻松接受。其实回过头看搬家也没有那么恐怖,我们之前遇到的问题是东西平铺开摆放没有规划,在收拾时就显得很多很杂;中介问题其实也不必过于担心受骗,大一点的中介公司还是很规范的。

Happy Moving.

Money

没钱,意味着失去了选择的能力。

缺少的不仅仅是选择的权利,还有为人生下赌注的资本。

你没钱,你放弃的成本就更高。

转两句从知乎看到的,然后提醒自己:

Money is like gasoline during a road trip. You don’t want to run out of gas on your trip, but you’re not doing a tour of gas stations. You have to pay attention to money, but it shouldn’t be about the money. — Tim O’Reilly

平滑升级 Nginx

Nginx 可以在不中断服务的情况下平滑升级,很是方便。

1.安装新版 Nginx,如果旧版本是编译安装可以通过 nginx -V 查看编译参数。默认会安装在同一目录,旧版本重命名为 nginx.old。

2.kill -USR2 old_nginx.pid,old_nginx.pid 会被重命名为 nginx.pid.oldbin,然后用新版 nginx 启动全新 master 和 worker。

3.现在新旧版本会同时服务,共同处理请求保证服务的不间断。kill -WINCH old_nginx.pid 来逐步关闭 old worker。

4.待 old worker 完全退出,新版本工作没有问题,用 kill -QUIT old_nginx.pid 完全退出旧版,nginx.pid.oldbin 会被自动更新为 new_nginx.pid,升级完成。

5.如果新版本有处理失败,需要回滚旧版,用 kill -HUP old_nginx.pid 重新启动 old worker,kill -QUIT new_nginx.pid 退出新版本。

More: Upgrading To a New Binary On The Fly

通过自签名 SSL 证书分发安装 IPA

iOS 7.1 通过 itms-services:// 安装 IPA 时要求 ipa.plist 必须 HTTPS 环境,不然会提示证书错误而无法安装。简单解决可以把 ipa.plist 放在 Dropbox 等支持 HTTPS 访问的地方,不过这样就不方便一键打包部署。其实可以通过自签名的 SSL 证书来解决这个问题。

1.创建自签名 CA 根证书,方便自动信任该 CA 所签发的证书:

1
2
openssl genrsa -out CA.key 2048
openssl req -x509 -new -key CA.key -out CA.cer -days 730 -subj /CN="Custom CA"

2.将 CA.cer 通过邮件等分发安装到设备作为信任证书。

3.创建 HTTPS URL 需要的密钥和证书:

1
2
3
openssl genrsa -out ipa.key 2048
openssl req -new -out ipa.req -key ipa.key -subj /CN=ipa.site.com
openssl x509 -req -in ipa.req -out ipa.cer -CAkey CA.key -CA CA.cer -days 365 -CAcreateserial -CAserial serial

4.上传 ipa.ceripa.key 到服务器,比如 /etc/nginx/ssl 目录下。

5.设置 Nginx 使用自签名证书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
    listen  443;
    server_name  ipa.site.com;

    ssl on;
    ssl_certificate /etc/nginx/ssl/ipa.cer;
    ssl_certificate_key /etc/nginx/ssl/ipa.key;

    location / {
        root /home/fannheyward/ipas;
        index  index.html index.htm index.php;
    }

    gzip on;
}

6.注意修改脚本里 ipa.plist 地址和 ipa 地址为 HTTPS.

iOS 7 Background Fetch

iOS 7 新加了三个后台任务 API: Background Fetch 后台获取,Silent Remote Notifications 静默推送,Background Transfer Service 后台传输。

Background Fetch 会由系统进行调度,应用可以在后台进行一定的网络请求。这里的限制是后台操作只允许 30s,超时未完成应用会被直接 kill,所以只适合做一些简单的网络请求。

Silent Remote Notifications 可以由服务端控制,通过消息后台打开应用根据消息内容 (content-id) 进行一些操作,也可以做网络请求,但同样只有 30s 限制。

Background Transfer Services 可以在后台进行网络大文件的下载、上传操作,没有时间限制,但只能在 Wi-Fi 下进行,而且受系统调度可能会是间断性进行。一般可以配合静默推送一起用,比如电视剧更新,静默推送最新一集信息到手机,应用后台新建下载任务然后逐步下载,下载完成后再通过 Local Notifications 通知用户观看。

Background Fetch 使用步骤:

1 在 Target - Capabilities 打开 Background Modes,勾选 Background Fetch。也可以手动修改 Info.plist 添加 UIBackgroundModes - fetch

2 设置后台获取时间间隔:

1
2
3
4
5
6
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

    return YES;
}

3 执行后台获取,并在完成后通知系统:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    //...
    [fetcher fetchDataWithResult:^(NSError *error, NSData *data){
        if (error) {
            completionHandler(UIBackgroundFetchResultFailed);
        } else {
            // parse data
            if (hasNewData) {
                completionHandler(UIBackgroundFetchResultNewData);
            } else {
                completionHandler(UIBackgroundFetchResultNoData);
            }
        }
    }];
}

需要注意的是一定要在请求完成后再调用 completionHandler();,不然请求有可能被系统中断。可以配合 NSOperation + KVO 在所有操作都完成后再执行 completionHandler();.

Xcode 5 提供了两个方法测试 Background Fetch,一是模拟器运行应用时通过 Xcode 菜单 Debug - Simulate Background Fetch 模拟;二是修改应用 Scheme 选中 Launch due to a background fetch event 再运行应用,这时候应用不会打开界面,真正的在后台运行。

参考 Multitasking in iOS 7, WWDC 2013 Session笔记 – iOS7中的多任务, iOS 7: Background Fetch.

NSOperation 笔记

iOS 下的多线程编程有 NSOperation 和 Grand Central Dispatch(GCD) 两种,简单记一些 NSOperation 的使用注意。

NSOperationQueue 相当于一个操作池,operation 添加进来后会按照 First-In-First-Out(FIFO) 的策略自动执行。operation 一般会添加到应用全局共享的自定义 queue,这样避免阻塞主线程的执行。

一些简单的多线程需求没必要动用 NSOperation 这个大家伙,NSInvocationOperation 就很方便:

1
2
3
4
5
6
7
8
9
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self
                                                                 selector:@selector(anyWork)
                                                                   object:nil];
[operationQueue addOperation:op];

- (void)anyWork
{
    //perform any work in operation
}

NSInvocationOperation 不是很方便共享操作,如果某个操作会在很多地方需要,就可以做个 NSOperation 子类封装:

1
2
3
4
5
6
7
8
@implementation CustomOperation

- (void)main
{
    //perform any work in operation
}

@end

这个子类只实现了 main 方法,相较 NSInvocationOperation 方便共享。如果需要对操作做更多细致化的功能,比如状态控制,就需要更加复杂的继承实现,参见 AFURLConnectionOperation,这种情况下不继承 main,而是继承实现 start cancel等,然后通过 KVO 手动控制操作状态的切换。

NSOperation 可以设置依赖,A 操作依赖 B 操作完成后才能做,那么就可以设置 B 为 A 的依赖 [A addDependency:B];.

如果各个操作之间没有依赖关系,但是又需要在全部操作都完成后做一些善后工作,有两个解决方案,一是添加所有操作为善后操作的依赖,这样所有其他操作完成后善后操作才会执行,这个方法较为死板,或者可以用 KVO 监听队列操作数,等操作都完成后队列操作为空的时候做善后工作:

1
2
3
4
5
6
7
8
9
10
11
12
[operationQueue addObserver:self forKeyPath:@"operationCount" options:0 context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == operationQueue && [keyPath isEqualToString:@"operationCount"]) {
        if (operationQueue.operationCount == 0) {
          // any final operation.
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

NSOperation or GCD

NSOperation 和 GCD 都能满足多线程需要,那么该选哪个?When to use NSOperation vs. GCD 的观点:

Always use the highest-level abstraction available to you, and drop down to lower-level abstractions when measurement shows that they are needed.

NSOperation 相比 GCD 提供了更多功能,比如操作执行状态,操作执行的暂停、取消,比如操作之间的依赖,比如控制操作队列同一时间可执行操作的数量。

GCD 相比 NSOperation 使用方便,系统开销小性能好。

实际项目中较为简单的小操作直接 GCD,灵活方便;规模较大控制复杂的操作还是通过 NSOperation 为好,也能享用高级 API 提供的方便。

Semantic Versioning 匹配

Semantic Versioning 直译为语义化版本,格式为 MAJOR.MINOR.PATCH,比如 1.2.3 代表第一主版本第二次版本第三补丁修正版,版本号唯一且可比较,更多信息参考官网介绍。

在 Node.js package.json 或 Bower bower.json 就是用 semver 做版本检查。记个笔记,假设当前最新版是 1.2.5:

  • 1.2.3 明确指定版本,需完全匹配,安装的版本就是 1.2.3.
  • >1.2.3 大于指定版本,匹配符合条件的最新版 1.2.5.
  • <1.2.3 小于指定版本,匹配符合条件的最新版 1.2.2.
  • <=1.2.3 可以包括补丁修正测试版,比如 1.2.3-beta,
  • 1.2.3 - 2.3.4 等同于 >=1.2.3 <=2.3.4.
  • ~1.2.3 接近于 1.2.3 的版本,等同于 >=1.2.3-0 <1.3.0-0,也就是相同主版本,相同次版本,补丁修正大于等于所需版本均符合。这种情况是实际使用中最多的,保持最大的兼容性.
  • ~1.2 等同于 >=1.2.0-0 <1.3.0-0,相同主版本、相同次版本均符合,等于 1.2.x
  • ~1 等同于 >=1.0.0-0 <2.0.0-0,相同主版本均可,等于 1.x.
  • * 任意版本,会匹配当前可用的最新版 1.2.5.
  • 1.2.3 || 1.3.2 哪个满足取哪个,如果两者均符合取第二个。

主要就是 ~ 匹配的使用,一般就用 ~1.2.3,保持次版本内最新,又保持最大兼容性。

参考 semver for npmpackage.json.