蓝牙开发之第一次:
忙完IOMix,还在研究iOS的音频框架,老板突然就说要先做蓝牙相关的项目了。于是就开始了第一次开发iOS蓝牙应用。两周时间,厘清了很多之前模糊的地方。
首先有三个事情需要说明:
1、BLE(蓝牙4.0)可以实现1连多(我做的项目就是1台手机连接8个蓝牙模块);
2、iOS这边是拿不到蓝牙模块的mac地址的(安卓可以)(网上说可以通过蓝牙模块额外的返回值传送);
3、iOS BLE的开发,用到的官方框架是CoreBluetooth。当然,会有人基于此封装成其它第三方框架(基本上是将官方的“代理回调”封装成“block回调”),此次开发就是用了第三方的封装框架。
框架的选择。一开始经理建议我可以使用YmsCoreBluetooth,不过我看它的星星数,就感觉有点儿不靠谱儿,后面使用,果不其然,有个坑一直跳不出去(好像是发送指令时peripheral对象一直为空,数据发不出去,当时对蓝牙的整个流程也不熟悉,所以坑没跳出去)。所以后面又上网查了一下,找到BabyBluetooth(有想过用回官方框架,不过2周时间,猜想是来不及了),运行它们demo,看起来不错。又按照demo自己写了一下,可以链接和发数据,于是就决定用这个了。
因为之前开发过Wi-Fi通讯类型的APP,所以对比着Wi-Fi来理解。一开始,我猜想:一个“服务”,会不会就是一条指令,而“服务”下一层的若干“特征”,就是每条指令里面的对应属性?后来又猜想,是不是一个“特征”对应一条指令?上面的猜想都是错的。那“服务”和“特征究竟是什么鬼?
现在手上接触到的蓝牙模块,都只有一个“服务”(我也不知道该对应socket(Wi-Fi)通讯里的哪部分内容),然后“服务”里面的若干“特征”,有一个“特征”的属性是“Data In”的,这个“特征”就是用于写入(发送)数据(指令)给硬件的,没错,就是无论你有多少条指令,都是通过这个“特征”写入。在我们的蓝牙模块中,要用到的“服务”的UUID(唯一标示符)是“FF12”,写入数据的这个“特征”是“FF01”。
所以,你如果要写入(发送)数据,在打包好指令(指令的定义、打包就和socket通讯的类似)后,找到peripheral对象(决定你要发给哪个蓝牙模块.在链接多个蓝牙的时候要鉴别确定,连结单个蓝牙就不需要了),找到“特征”,再利用peripheral对象调用writeValue: forCharacteristic: type:方法(或者说发送writeValue: forCharacteristic: type:消息给peripheral对象),第1个参数传指令内容,第2个参数传写入的“特征”对象,第3个参数传是否有回应。是不是很明白清晰?
所以,(写入)“特征”它就像一个管道、一个通道,表示可以通过它来进行指令的写入,无论你有多少指令,都是利用这个“特征”发送。
而硬件返回的数据,就不是走这个“通道了”,它另外有一个“Data Out”的“特征”,这个“特征”专门负责数据从模块发出(发给连结的手机)。只要“监听”了这个“特征”,就能收到从模块发出的数据了。所以,和走tcp传输的Wi-Fi不同,tcp发送和接收数据,都是在同一个“通道”进行;而蓝牙,则数据发送用一个“特征”,数据接收,又用另外一个“特征”,是两个不同的“管道”(容许我暂时这么理解)。
上面说到用writeValue: forCharacteristic: type:方法发送指令,好像没有用到“服务”,那“服务”还有什么用?用于发送数据时找“特征”,因为“服务”和“特征”是树状结构,要找到“特征”,就必须通过“服务”。
另外,writeValue: forCharacteristic: type:的第三个参数,其实只有两种情况,一个是有返回值的CBCharacteristicWriteWithResponse,另外一个是没有返回值的CBCharacteristicWriteWithoutResponse。这个参数可不是你想写哪个就写哪个,要决定于该“特征”的属性(CBCharacteristicProperties类型),有10种可能。CBCharacteristicPropertyWriteWithoutResponse对应的是CBCharacteristicWriteWithoutResponse,CBCharacteristicPropertyWrite对应的是CBCharacteristicWriteWithResponse,不能写错,否则就会发不出去指令。我就之前就掉进这个坑一次。
对iOS中“设计模式”的进一步理解:
随着写项目的数量提升,再回头去看MVC,又有了深一点的认识。
在项目实践中,我发现很难严格遵守MVC模式,当然我不是指大家把MVC三者搞混,而是大家都会想方设法给控制器“C”减肥,将一些其它功能独立出控制器之外。所以无论是《iOS编程》书中提及到的MVCS,还是在网上大家经常讨论的MVVM,两者的目标应该是一致的——给控制器“C”瘦身。
其实从经理写Device类(负责实现和硬件通讯功能的类)开始,项目就不是严格意义上的MVC模式了,它将“负责和硬件通讯的功能”从控制器“V”中独立开来。然后我又将“负责数据本地保存的功能”独立到Store类中。再用一个单例“持有”它们,其它控制器通过这个单例获取对应的能力(和硬件通讯、保存数据)或获取数据。
所以我们以后和硬件通讯类型的APP,我们项目的组织模式可以说是MVCDS了:D负责和硬件通讯,S负责保存数据。也正是用了这种“设计”(其实我并不是事先设计好的),才能实现和同事的分工合作:我专心写和硬件通讯的功能;同事专心实现UI。
而关于MVVM这种模式,这两天看到有人翻译的国外一篇《ReactiveCocoa 和 MVVM 入门》的文章,终于有了一点初步认识(之前看的文章,都是不明所以),文章用一个图示说明VM(view model)是分别从控制器“C”及视图“V”中瘦身出来的一部分内容(“C”的占大部分)。所以,我可以从另一个角度去理解MVVM了:VM也可以看作是对“C”瘦身出来的内容,就类似我们的项目瘦身出“D”和“S”的内容,只是大家的瘦身方式不一样,而且VM还包含了部分“V”的内容(不知道是否就是ReactiveCocoa这一部分涉及到的,后续再深入研究)