第三方即时通讯服务

越来越多的移动 App 加入了即时通讯功能,目前也有越来越多的第三方即时通讯服务供开发者选择。如 环信融云阿里悟空容联云通讯LeanCloudBmob 等。

环信,最早的专门做移动通讯服务的团队之一。其功能很丰富,具有完善的用户系统、群系统等,也是为数不多能提供视频通话的服务。Demo 完成度也很高,但是工程结构一般。其通讯核心部分封装为了环信 SDK,以 .a 文件的方式打包发布,同时提供 cocoapods 方式引入(但更新很不及时)。UI 部分并未进行很好的组织,仅是在 Demo 中以源代码的形式提供,且代码质量一般。如果要调整 UI,只能在 Demo 源代码的基础上修改,或者自己实现。

融云,功能丰富、Demo 完成度高。迭代速度极快,每周更新一次。通讯核心部分和 UI 部分分别封装为了 IMLib 和 IMKit 两个库,工程结构良好。实现 UI 时,可以继承 IMKit 中的类,如有需要可在子类中进行调整。当然也可以只用 IMKit,自己实现 UI。与环信不同的是,它并没有用户系统,并不存储任何与用户相关的信息。这样设计的初衷在于,当一个应用需要集成即时通讯服务时,往往应用本身的服务器已经有一个用户系统了。所以对于应用的每一个注册用户,应用服务器不需要再申请注册一个新的融云用户,而只需要直接采用用户 ID 作为通讯的身份即可,用户信息全部都从自己的应用服务器中维护。

阿里悟空为阿里来往所采用的即时通讯服务,2015 年将服务对外开放并开发了移动端 SDK。目前功能较简单,Demo 完整程度较低。仅提供了核心通讯功能的封装,Demo 中的 UI 部分很简陋,只能作为参考。其也不存在用户系统,OpenID 的方式与融云的用户管理方式相似。

容联云通讯,包含短信验证码、即时通讯、音视频通话等,短信验证码的服务客户很多。腾讯云的音视频云通信 AVC,今年刚开放,主要优势在于音视频通讯。

LeanCloud 和 Bmob,是集多种应用服务为一身的后端服务。包括数据存储服务、消息推送服务、实时通信服务等。

UIAlertView without title

UIAlertView 是支持无标题的。如果传空字符串,即为没有标题,只显示内容;如果传 nil,则会把内容放到标题上。

1、title 传空字符串,即:

1
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"" message:@"您确定要退出此帐号吗?" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确认", nil];

2、title 传 nil,即:

1
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:nil message:@"您确定要退出此帐号吗?" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确认", nil];

另外,如果需要自定义 AlertView 的样式,可以采用开源的 LGAlertView

MBProgressHUD and UITableView section header

在一个 UITableView 上添加一个 MBProgressHUD,很有可能会遇到这种情况,HUD 被 TableView 的 section header 挡住了:

同样的,如果你用了 UITableViewController,则 self.view 就是一个 UITableView,则当向 self.view 添加一个 HUD 为 subview 时,就会遇到这个问题。

在 github 上的 issue:
https://github.com/jdg/MBProgressHUD/issues/242

在 stackoverflow 上的讨论:
http://stackoverflow.com/questions/7199700/uitableview-headings-shown-on-top-of-mbprogresshud

可以根据情况考虑,选择下面的方法:

  1. 不用 UITableViewController,改用 UIViewController + TableView,并把 hud 添加到 self.view 上。
  2. 把 hud 添加到 self.navigationController.view 上,这样会带来一个副作用,hud 显示的时候不能点击返回按钮,不推荐。
  3. 调整 hud 的 z 值,hud.layer.zPosition = MAXFLOAT; (z 值越大越在上方),推荐采用。

限制 UITextView 最大输入字符数

实现以下两个 UITextView 的代理方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define LENGTH_LIMIT 10
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if (textView.text.length >= LENGTH_LIMIT && text.length > 0) {
return NO;
}
return YES;
}
-(void)textViewDidChange:(UITextView *)textView {
if (textView.text.length > LENGTH_LIMIT) {
textView.text = [textView.text substringToIndex:LENGTH_LIMIT];
}
}

注意,这两个代理方法必须都实现。

如果只实现第一个代理方法,则会出现一个问题:到达字数限制时,用键盘的确已无法输入进去,但可以通过点击键盘上方的联想字词继续输入进去,导致超过限制。通过实现第二个代理方法,可把通过联想或其他方式输入进去的多余字符截掉。这也是之前我没有注意到的一点。

如果只实现第二个代理方法,虽然长度不会超过限制,但实际相当于先输入进去,后再截掉,导致按下键盘后虽然没有输入进去字符,但键盘上方的联想字词在不断地根据你的按键而联想。

iOS 8.2 UITableView 的默认行高 bug

Cell高度错误

很简单的一个登录页面,用 Storyboard 的 Static Cell 来实现。开发时没有发现任何问题,交予测试时反馈输入框太小了,就是上面截图的样子,现象 100% 出现。而且发现,从该页面 Push 到下一层页面再 Pop 回该页面后,高度即可恢复正常。

检查程序没发现任何问题,Cell Height 都采用了默认高度。在我的模拟器上和其他测试设备上也无法重现这个问题。

检查那台出问题的测试机,发现系统版本为 iOS 8.2,而我的模拟器和测试机上都运行着 iOS 8.3 或 iOS 7.1。于是安装 iOS 8.2 模拟器,问题在 8.2 版本的模拟器上重现了!

经研究,推测为 iOS 8.2 的系统 bug,只要是采用了 TableView 的默认行高,就会出现行高不正确的问题。这个 bug 已经在 iOS 8.3 上得到修复。若要确保 iOS 8.2 系统下也不会出现显示异常,可以采用以下任意一种方式:

  1. 把 TableView 的 Row Height 属性设置为不是默认 44 的值。
  2. 把 TableView 中的 TableViewCell 的 Row Height 属性,选中 Custom。

Updated to Hexo 3 from 2

我一直在用的静态博客框架 Hexo,在今年 3 月份大版本升级到了 3.0 版。一直没有从 2.x 升级到 3.x,今天升级并迁移到了最新的 3.1.1,了解了一下 3.x 的变化。
官方提供了迁移文档,升级过程很顺畅,安装 hexo-cli 时果然如文档所说 “error occurred”,把老版本的 hexo 先删掉就好。

有一个地方文档中并未提及,需要格外注意:除了安装 hexo-cli 之外的步骤,都应该在你的 blog 根目录下进行。这也反映出了新版本最大的变化:模块分离。

首先可以看到,新版本多了一个 hexo-cli,也就是 Hexo command line interface,这也是新版本中唯一需要安装到系统全局的模块。把 CLI 从主模块中分离,可以让 CLI 加载更快,同时能够允许你在不同的目录下维护着不同版本的 hexo blog。

而其他所有模块,包括 hexo, hexo-server, hexo-deployer, hexo-generator, hexo-renderer 等,都被安装在某一个 blog 的 node_modules 目录下,仅应用于这一个 blog。

模块分离有利有弊,但大多数情形下是利大于弊的。对于 hexo 来讲,带来最大的利就是,blog 目录下包含着所有需要的内容和程序,受系统环境的限制就能做到尽可能的小。而先前版本的 hexo,blog 目录中仅包含所有的内容,而并不包含程序 – 程序需要调用系统中安装的。设想一下,Alice 在 Bob 家度假,突然想用 Bob 的个人电脑写个博客(假设 Alice 没有带自己的笔记本)。Alice 平常用的是 2.x 版本的 hexo,并把 blog 目录同步到了 Dropbox。而 Bob 用的是 2.y 版本的 hexo,2.x 和 2.y 版本是不完全兼容的。于是,当 Alice 在自己的 blog 目录下执行 hexo 的命令时,可能会出现一些意想不到的错误 – 内容是 2.x 版本的,而程序是 2.y 版本的。

而如果它们都用了现在模块分离程度更高的版本,那么在 Alice 的 blog 目录中,就同时包含了 3.x 版本的内容和 3.x 版本的程序,虽然 Bob 的电脑上是 3.y 版本,但这并不影响什么 – 因为唯一需要依赖系统的是 hexo-cli,而这个 hexo-cli 只是一个 driver,具体的功能实现都将由 blog 目录中的程序接管。而由于这个 hexo-cli 功能非常简单,所以可以做到非常稳定,不同的版本基本上可以完全兼容。

这样的大版本更新也是有些副作用的,比如由于 hexo-deployer-git 模块也改变了,之前的 git 提交历史都没有了…

WWDC 2015

今年的 WWDC 没有什么让人兴奋的看点,没有硬件新品,没有突破性改变的软件新品,所带来的都是些稳定性的提高、细节的完善、功能的增强。但其实这也正是苹果目前最应该做的,步子大容易扯着蛋,这句话在 OS X Yosemite 和 iOS 8 上得到了验证。

New OS

iOS 9,OS X EI Capitan, watch OS 2.

iOS 9,最让人没想到的是,它竟然还支持 iPhone 4s 和 iPad 2。按照以往的规则,iPhone 4s 和 iPad 2 今年应该被最新版的系统抛弃了。据说苹果对 iOS 9 从底层进行了大量的改造,大幅提高了系统的流畅度和稳定性,所以可以让老设备焕发第二春。可能是因为老设备运行 iOS 8 实在是太不堪入目,苹果想挽回一点儿面子?我迫不及待地把手中已经卡成翔的 iPhone 4s 升级到了 iOS 9 Developer Preview,用了两天,果断降回来了–频繁死机、卡顿,完全没法用,现阶段大家就不要尝试了。放张图给大家看看吧。
iOS 9 Developer Preview

iOS 9 在功能上最大的革新,是 iPad 上支持了多任务分屏,有 Slide Over、Split View、画中画三种模式。再就是 Siri 的搜索更加智能,会根据使用习惯给出建议。其他的都是些小变化了,比如地图支持公交、备忘录变成了个富文本编辑器、Passbook 变成了 Wallet。增加了两个新应用:News 和 Apple Music,估计在国内也没什么用。

对了,观感上的变化就是换了新的默认字体,英文新字体 San Francisco,中文新字体苹方,看上去还蛮不适应。

EI Capitan 没给我留下什么印象,还是好好改 bug 吧,把 bug 改好就挺好了…watch OS 2 也没啥说的,好好支持原生应用吧…

New tools

Xcode 7, Swift 2, New developer program.

Xcode 7,竟然对在设备上调试不再做限制了,不再需要开发者身份就能直接将程序在任意设备上运行。这着实降低了开发者的入门门槛,但我比较担心这样会不会有空子可钻,毕竟 iOS 平台的应用安全就是靠着层层限制所保证的,一旦 Xcode 这种毫无限制的 Adhoc 被钻了空子,还是很棘手的。

Xcode 7 run on device

Swift 发布了 2.0 版,已经日臻成熟了。

一个令人兴奋的消息是,Developer Program 以后不再区分 iOS 和 Mac,之前购买的也将升级为新的 Apple Developer Program。以后只需要一个 99 美金就可以同时上架 iOS App Store 和 Mac App Store 了。

New Apple Developer Program

后记

今天看到篇文章,抨击之前 OS X 的中文默认字体,看着还有点意思:OS X / iOS 六年字体战争

Google I/O 2015

Google I/O 2015 召开了,虽说今年的 Google I/O 没有像去年一样带来很多新东西,但这次大会不乏让人眼前一亮的内容。

首先是 Android M,终于把之前粗放的权限管理进行了重新设计,不再像之前那样只是在安装应用的时候通知你一下,而是借鉴了 iOS 更为科学的权限管理方式。Android 最大的问题之一就是权限,希望这次可以真正得到解决。Android M 原生支持了指纹识别,支持了 64 位架构,别的好像也没什么。

新推出的 Google Photos 看起来蛮不错,自然语言搜索貌似很强大。可以存储无限容量的照片,重要的是免费。当然有些人说之所以免费,是为了要搜集更大量的信息,以提高数据分析的能力。

还有个很有意思的东西,Cardboard,一个很接地气的 VR 装置,不得不佩服 Google 工程师们在 20% 时间里发挥出的聪明才智。

“cannot load such file -- openssl” while “gem install”

工作用的 iMac 上一直没有安装 CocoaPods,因为曾经安装时遇到了一个错误,然后比较忙就搁置了,以至于搁置到了现在,今天终于把这遗留问题解决了。

CocoaPods 是应用于 Xcode 工程的第三方库依赖管理工具,可以非常方便地管理工程所需要引用的第三方库。由于它是用 ruby 开发的,所以在 Mac 上安装它是一个非常简单的事情:

1
$ sudo gem install cocoapods

但是当我执行这个命令时,却得到了错误的反馈:

1
2
3
4
ERROR: Loading command: install (LoadError)
cannot load such file -- openssl
ERROR: While executing gem ... (NoMethodError)
undefined method `invoke_with_build_args' for nil:NilClass

其实此时不只是安装 CocoaPods,安装其他大部分的 gem 都会是这个结局,因为找不到 openssl,无法建立加密连接,也就无法开始下载。

系统中是有 openssl 的,而且 openssl 可以正常工作,只是 ruby 找不到它。至于为什么突然找不到了,那就不太清楚了,推测可能是更新 OS X 时引发的问题吧。试了很多方法,各种不奏效,最后通过以下的方法解决了。大体思路是用 rvm 重新安装 openssl,然后重新安装 ruby,并指定 openssl 的路径。

1
2
rvm pkg install openssl
rvm reinstall ruby-2.1 --with-openssl-dir=$rvm_path/usr

都安装完成之后,就可以正常 gem install 啦。

百度地图 (iOS SDK) 内存不回收

情景:有一个列表,点击列表中的某一条目后,进入详情页面,详情页面是一个地图。

环境:百度地图 iOS SDK v2.7.0

测试人员发现,从列表反复多次进入详情(push, pop, push, pop, …),五六次后程序崩溃。查看日志,发现是 Received Memory Warning。

通过 Xcode 的 Debug Navigator 监测,发现每进入一次地图页面,内存即增加占用约 20MB,但返回后内存并不回收。于是内存占用逐渐增多,直至被 kill。

继续监测程序的运行,发现地图页面在 pop 时,dealloc 方法并没有被执行,所以整个详情页面,包括地图,一直没有从内存中被 dealloc。

最终发现,除了百度地图的 mapView,在 self.view 上还有另一个 subview,而正因为这个 subview 没有 removeFromSuperView,导致了内存一直不能被回收。

含有百度地图的 viewWillDisappear 里一般是这样写的:

1
2
3
4
5
- (void)viewWillDisappear:(BOOL)animated {
[_bmkMapView viewWillDisappear];
[_bmkMapView setDelegate:nil];
[super viewWillDisappear:animated];
}

但此时需要这样写:

1
2
3
4
5
6
7
8
- (void)viewWillDisappear:(BOOL)animated {
[_bmkMapView viewWillDisappear];
[_bmkMapView setDelegate:nil];
for (UIView *view in [self.view subviews]) {
[view removeFromSuperview];
}
[super viewWillDisappear:animated];
}

另外,还有一些其他的情形也可能造成此类问题,基本原理是一致的,都是因为还有某个对象被引用着。内存的管理还是得加小心啊。