tripleCC的技术博客

ʕ•̫͡•ʔ-̫͡-ʕ•͓͡•ʔ-̫͡-ʕ•̫͡•ʔ-̫͡-ʕ•͓͡•ʔ-̫͡-ʔ

RxSwift实现初探

和ReactiveCocoa的实现类似,RxSwift也是通过不停地订阅上游的Observable来实现数据的流动。
Rx操作大体分为两种:

  • 创建: create、just、of、from等
  • 处理: map、flatMap、do等

接下来通过下面的操作来简单分析下代码执行过程:

1
2
3
4
5
let _ = Observable.just(1)
    .map { $0 }
    .subscribe(onNext: {
    print($0)
})

iOS知识碎片七

1、Xcode8日志及Pod的Swift3.0问题
2、UITextFiled使用UITextAlignmentRight输入空格光标不实时跟进
3、解决使用UIAlertController时,Attempt to present vc whose view is not in the window hierarchy! 问题
4、单例在 Unit Test 中不唯一问题

利用策略模式增强图片浏览器的扩展性

说到图片浏览器,项目中比较常用的成熟框架有Objective-C版本的MWPhotoBrowserIDMPhotoBrowser或者Swift版本的SKPhotoBrowser

从核心功能来看,MWPhotoBrowser,IDMPhotoBrowser这两个框架,都很好地实现了对本地资源、相册资源、网络资源的获取与显示。并且很好地封装了网络和相册的获取方式,在我看来,这是他的优势,但同时,高度的集成也催生了一些不足。

这样做的优势不言而喻,调用者只需要很少的几行代码,就可以集成一个图片浏览器框架,省时省力。以MWPhotoBrowser为例,在不设置额外属性的情况下,只需要下面两行代码就可以创建:

1
2
MWPhotoBrowser *browser = [[MWPhotoBrowser alloc] initWithPhotos:self.photos];
[self.navigationController pushViewController:browser animated:YES];

使用者只要关注如何提供MWPhotoBrowser所要展示资源就可以了,不需要做额外的操作,非常地简洁方便。

关于不足,由于MWPhotoBrowser内部实现了获取网络图片功能,在追求内部实现尽量精简的前提下,不可避免地要依赖加载图片的第三方库(SDWebImage)。如果原来项目并没有使用SDWebImage,而是用YYWebImage或者Kingfisher,那么使用MWPhotoBrowser便会引入冗余的框架,从而让项目额外增加了一种图片缓存机制,不利于内存以及磁盘使用率的优化。

对于相册资源的访问,MWPhotoBrowser内部也实现了通过PHAsset或者ALAsset获取相片的功能。不过一般来说,项目会有自己的一套相册选择器,进而会有相应的相册资源获取策略。所以以个人观点来看,如何获取相册资源,应该由使用者告知,而不是在框架内部自己实现一套,这样更加符合DRY。

接下来,我会针对上面的不足,实现一套兼容本地资源、相册资源、网络资源的简易图片选择器。
本文章对应的所有代码在仓库TBVImageBrowser中。

使用AssetsLibrary和PhotoKit做一个简易的相片选择器

iOS8之后,苹果推出了PhotoKit,让开发者在处理相册相关的业务时,可以更加得心应手。github上的开发者针对PhotoKit做了一层很优秀的封装CTAssetsPickerController,如果只需要支持iOS8+,那么可定制程度非常高的CTAssetsPickerController是个不错的选择。
但是由于现有的业务还是需要支持iOS7,所以并不能完全舍弃使用AssetsLibrary的方式来访问相册。因此也就需要自己封装一套兼容iOS7的相册管理器。

本文涉及代码:TBVAssetsPicker

iOS知识碎片六

1、dispatch_after及NSTimer造成对象无法释放
2、宏定义###
3、Swift实现原子属性
4、Xcode图像化调试错误
5、UITabBarController调用viewDidLoad的时机

YYModel阅读小记

YYModel是由ibireme开发的一套小而精美的模型转换框架,采用分类的形式,无需继承框架的某个基类就可以方便地完成模型的转换,且内部做了自动类型转换和安全处理,可以有效地防止因模型类型和后台给的数据类型不一样而产生的崩溃问题。
近些天抽空拜读了一下其源码,果然是思维严谨,考虑的一些细节也很到位,让人自叹弗如。虽然作者说这个框架是花了两个周末的时间完成的,但是其代码质量还是非常让人惊艳的,值得仔细阅读。

MQTT使用小记

MQTT全称Message Queue Telemetry Transport,是一个针对轻量级的发布/订阅式消息传输场景的协议,同时也是被推崇的物联网传输协议。MQTT详细的介绍文章可以从官方网站获得,所以这里就不进行详细的展开了,而是针对这些天的使用经历与感受做一番纪录。

MQTT开源的iOS客户端有以下几种:

1
2
3
4
|MQTTKit  |Marquette|Moscapsule|Musqueteer|MQTT-Client|MqttSDK|CocoaMQTT|
|---------|---------|----------|----------|-----------|-------|---------|
|Obj-C    |Obj-C    |Swift     |Obj-C     |Obj-C      |Obj-C  |Swift    |
|Mosquitto|Mosquitto|Mosquitto |Mosquitto |native     |native |native   |

以上开源库我只看过部分MQTTKit、MQTT-Client、CocoaMQTT的开源代码,总体来说MQTT-Client支持的功能更多全面一些。如果只是对协议本身进行学习不考虑功能的话,可以阅读CocoaMQTT,也可以阅读我重写的SwiftMQTT,因为代码量相对前面两个库少了很多。

而MQTT的broker一般选择Mosquitto,Mosquitto是一个由C编写的集客户端和服务端为一体的开源项目,所以相对来说风格较为友好,可以无障碍地阅读并调试源码(开源地址)。可以看到,以上客户端开源库中的前四种就是基于Mosquitto的一层封装。

更加快速地设置Frame

由于现在手头上的项目是基于frame开发的,没有xib或者storyboard,没有使用自动布局,所以排布界面时总是显得很繁琐。

令人蛋疼的frame布局

老代码对界面的坐标尺寸设置都是通过下面的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
UIView * mainView = [[UIView alloc] initWithFrame:CGRectMake(0, self.height, self.width, MAIN_HEIGHT)];
[mainView setBackgroundColor:[UIColor whiteColor]];
[self addSubview:mainView];
self.mainView = mainView;

UIView * opertionMenu = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.width, 45)];
[opertionMenu setBackgroundColor:[UIColor whiteColor]];
[mainView addSubview:opertionMenu];

UIView * line = [[UIView alloc] initWithFrame:CGRectMake(0, 44, self.width, 1)];
[line setBackgroundColor:[UIColor colorWithHex:0xe9e9e9]];
[opertionMenu addSubview:line];
...

这些坐标设置工作都是在初始化,也就是init系列方法中完成的。这样做的弊端很明显,复用性很差,如果还是按照这种方式的话,每扩展一种界面类型,就要新增一个init方法。久而久之,冗余代码会越来越多,新增特性想重用这块控件的话,需要做比较多的无用功。

SDWebImage设置大尺寸图片崩溃问题

昨天产品在teambition上提了一个bug:点击特定的页面app闪退。
我很是纳闷,因为通过其它类型索引进入的详情页面都不会出现这样的情况,为什么偏偏是这个页面?还是因为memory warning而闪退?而且内存不是慢慢增加,而是从80M左右激增到600M+
接着我查看了进入这个页面时获取的json,仔细观察后,发现并没有特别的地方。于是我决定使用instruments的Allocations查看到底是什么操作占用了如此庞大的内存。
进入界面之后,展示的界面如下图:

iOS知识碎片五

1、iOS中文斜体
2、Swift中替代#ifdef以及关于Target管理
3、UIRefreshControl下拉抖动
4、聊天界面输入框换行抖动
5、UITapGestureRecognizer与tableView:didSelectRowAtIndexPath: