按照文档一步一步来
新建一个基于单页面工程,然后新建一个一个Target,选中Cocoa Touch Framework。然后,分别新建一个Swift文件和Objective C类,注意Target Member Ship选中Framework。类的内容如下:
OCSourceh
[上传失败(image-8dab68-1634619754604)]
OCSourcem
[上传失败(image-2c9071-1634619754604)]
Swift调用OC
新建SwiftSourceswift
[上传失败(image-34eca8-1634619754604)]
然后,按照文档中,为了让Swift文件访问Objective C文件,我们应该在umbrella header,也就是MixFrameworkh中,暴露所需要的header。
也就是,MixFrameworkh,
[上传失败(image-b166c3-1634619754604)]
然后,自信满满的点击build。
Boom~~~,编译不通过。
[上传失败(image-6ca763-1634619754604)]
原因:OCSourceh默认编译的时候是Project权限 为了在umbrella header中使用,要把这个文件的权限改成Public
按照图中的方式拖过去即可。
[上传失败(image-f2d31d-1634619754604)]
嗯,现在build,可以看到build成功了。
OC调用Swift
在SwiftSourceswift中,增加一个类,
[上传失败(image-98ce8c-1634619754604)]
然后,为了在OC中调用Swift的方法,我们需要导入头文件,这时候,OCSourcem文件内容如下
[上传失败(image-f61fb3-1634619754604)]
然后,build,发现成功了,很开心。
外部调用
在ViewControllerswift中,我们调用Framework中的内容。
[上传失败(image-6be19c-1634619754604)]
然后运行,发现控制台打印出
[上传失败(image-4b70-1634619754604)]
嗯,framework打包成功了。
问题
通常,我们希望暴露给外部的接口是纯Swift,而OC文件的具体接口应该隐藏,这就是我标题中的优雅两个字的含义。
如果你好奇,你会发现,在ViewControllerswift中你可以这么调用
[上传失败(image-fefccf-1634619754604)]
也就是说,OC的内容也暴露出来了,这破坏了Framework的封装特性。
通过查看MixFramework的编译结果,发现最后暴露出的接口是这样子的
[上传失败(image-8b0488-1634619754604)]
这一行,把OC对应的实现暴露出来了
[上传失败(image-9c74a7-1634619754604)]
优雅的解决方案
不再通过umbrella header的方式让framework中的Swift调用OC方法。而是通过modulemap。
新建一个modulemodulemap文件,内容如下
[上传失败(image-633a91-1634619754604)]
[上传失败(image-d30f90-1634619754604)]
这里的#(SRCROOT)是XCode的宏,会自动替换成项目所在的根目录,这里输入的路径是modulemodulemap文件所在的路径。
然后,删除MixFrameworkh(umbrella header)中#import 的OC header。
把OCSourceh的权限改回默认的project。
[上传失败(image-291cca-1634619754604)]
再编译,发现OC的类被隐藏了。
首先,请确保PerfectLib已经在您的Swift源程序开始部分完成声明:
import PerfectLib
声明完成之后,即可开始使用File文件对象来实现在文件系统中的查询和文件 *** 作。
配置File文件对象
使用File文件对象时,请指定文件的绝对或相对路径:
let thisFile = File("/path/to/file/helloWorldtxt")
如果您对文件路径还不熟悉,请先阅读目录与路径
打开文件用于读写 *** 作
注意: 在写入文件 *** 作之前(即使是个新文件)文件需要对应的权限才能打开。
打开一个文件:
try thisFileopen(<OpenMode>,permissions:<PermissionMode>)
写入文件的例子:
let thisFile = File("helloWorldtxt")
try thisFileopen(readWrite)
try thisFilewrite(string: "你好!")
thisFileclose()
对于文件打开的具体模式OpenMode 和文件权限模式 PermissionMode ,请参考本文附录的详细定义。
检查文件是否存在
请使用exists方法检查文件是否存在。该方法返回一个布尔值,真表示存在,假表示不存在。
thisFileexists
获取文件修改时间
调用下面的函数将返回一个整数,其含义为自格林威治时间1970/01/01 00:00:00到最后一次修改文件的时间之间的秒数:
thisFilemodificationTime
文件路径
无论文件参考对象是如何定义的,文件所在的绝对路径和内部参考路径都可以通过以下访问获取具体信息:
返回系统内部参考路径"internal reference":
thisFilepath
返回文件的绝对路径(物理路径)方法(如果当前文件为符号链接,则同样其链接也会被解析为绝对路径):
thisFilerealPath
关闭一个文件
一旦文件被打开并执行读写 *** 作,随时可以在程序内选择用close方法关闭,或使用defer方法挂起:
let thisFile = File("/path/to/file/helloWorldtxt")
// 在此处进行文件读写 *** 作处理
thisFileclose()
删除一个文件
如果需要从文件系统中删除一个文件,请使用delete()方法。
thisFiledelete()
调用删除方法时会自动关闭文件,因此不需要提前进行额外的close()关闭 *** 作。
查询文件大小尺寸
调用size方法可以返回文件大小的字节数,返回值为整数。
thisFilesize
判断文件是否为一个符号链接
用下面的方法来判断当前文件对象是否为一个符号链接,如果是符号链接则返回值为布尔类型的true,真值,反之为false假。
thisFileisLink
判断当前文件对象是否为一个目录
用isDir 方法来判断当前文件对象是否为一个目录节点,如果是则返回值为布尔类型的true,真值,反之为false假。
thisFileisDir
获取文件的UNIX权限信息
调用perms 函数可返回文件的权限信息,返回值为一个PermissionMode对象。
thisFileperms
比如:
print(thisFileperms)
>> PermissionMode(rawValue: 29092)
读取文件内容
readSomeBytes
根据指定的字节数量读取文件内容:
let thisFile = File("/path/to/file/helloWorldtxt")
let contents = try thisFilereadSomeBytes(count: <Int>)
参数说明
该方法的参数为计划读取的字节数量。比如,下面的例子说明了如何从文件中读取10字节数据:
let thisFile = File("/path/to/file/helloWorldtxt")
let contents = try thisFilereadSomeBytes(count: 10)
print(contents)
>> [35, 32, 80, 101, 114, 102, 101, 99, 116, 84]
readString
readString方法能够将整个文件的数据读取到一个字符串:
let thisFile = File("/path/to/file/helloWorldtxt")
let contents = try thisFilereadString()
文件写 *** 作、拷贝和移动
注意 在写文件之前(即使是一个新文件)必须要确认文件 *** 作具备响应的权限。
向文件内写入字符串
使用write方法以UTF-8编码格式向文件创建或写入一个字符串。如果写入成功,则返回具体写入的字节数。
注意该方法会使用@discardableResult属性,所以如果需要的话,即便没有对该属性赋值,也会在调用过程中被使用。
let bytesWritten = try thisFilewrite(string: <String>)
将字节数组写入到一个文件
字节数组bytes同样可以直接写入到一个文件。该方法如果调用成功则返回实际写入的字节数。
注意该方法会使用@discardableResult属性,所以如果需要的话,即便没有对该属性赋值,也会在调用过程中被使用。
let bytesWritten = try thisFilewrite(
bytes: <[UInt8]>,
dataPosition: <Int>,
length: <Int>
)
参数说明
bytes: 待写入的无符号8位整型数组
dataPosition: 可选参数 在bytes对象中待写入字节的偏移量。如果该参数不为0,则写入时从字节数组的该参数指定字节处开始写入,之前内容会被忽略
length: 可选参数 需要写入的具体字节数量
移动一个文件
文件对象一旦定义成功,就可以随时调用moveto方法,将文件移动到一个新的路径位置上。该方法也可以用于给文件改名。 *** 作完成后,该方法返回一个代表新位置的新的文件对象。
let newFile = thisFilemoveTo(path: <String>, overWrite: <Bool>)
参数说明
path: 移动的目标路径
overWrite: 可选参数 如果目标路径文件已经存在,则进行覆盖 *** 作。默认值为假false
公司项目需要用到BLE以CBCentralManager的身份和硬件交互,开发过程中解决了一些遇到的问题和一些处理思路,这里简单记录一下。如果有什么问题或写的不对的地方希望大家可以一起讨论。
首先了解一下什么是BLE,蓝牙低能耗(Bluetooth Low Energy,或称Bluetooth LE、BLE,旧商标Bluetooth Smart,蓝牙版本40),也称低功耗蓝牙。相较经典蓝牙(蓝牙版本20),低功耗蓝牙旨在保持同等通信范围的同时显著降低功耗和成本。
与设备的交互使用的是16进制,所以要对发送的数据进行16进制转换,转换方法放在末尾
连接和 *** 作一个设备就要持有这个设备对象,系统不维护设备对象的内存管理
发送数据异步回调可以封装一个任务机制,发送数据后生成一个任务,在收到想要的数据的时候关闭任务或者等待任务超时关闭任务。
iOS更换手机的时候设备的UUID会改变,如果想换手机后依然可以重连设备,就需要让设备端配合把设备唯一MAC地址放入广播内容中,给设备扩充MAC属性,根据MAC来选择设备进行连接,做到设备MAC和UUID的匹配
本篇只做了简单的功能介绍和使用,OTA部分需要按照实际协议来做。如果大家有遇到问题或者有好的主意可以找我一起讨论,万分荣幸。
iOS对蓝牙库进行了封装,封装在CoreBluetooth库,所以使用时
接下来是对一些名词的介绍
大致结构如下
注:一个CBPeripheral可以包含多个CBService ,而一个CBService 也可以包含多个CBCharacteristic 。
接下来介绍蓝牙从打开到连接到发送数据到接收数据的一整个流程
1首先肯定是权限设置,Infoplist里面加入
Privacy - Bluetooth Peripheral Usage Description
2然后是初始化中心管理者,初始化有三种方式,我使用的默认的初始化方法即
如果想自己设置线程和其他条件,则可以通过接下来的初始化方法一次性进行设置
3判断蓝牙状态,通过CBCentralManager的state来获取
4如果状态为打开,则可以进行搜索 *** 作
注:如果连接和 *** 作一个设备就要持有这个设备对象,系统不维护设备对象的内存管理
接下来就是一系列的代理事件了,我会把主要代理按照流程来进行说明,大致流程如下:
搜索-连接-连接成功/失败(设置外设代理,搜索服务)-搜索到服务(搜索特征)-搜索到特征-监听需要的特征(读写、读、写等根据情况来确定)-通过外设读写特征写入指令-收到设备返回信息-断开连接
接下来对每个代理来进行详细介绍
CBCentralManagerDelegate:中心管理者代理,负责搜索,设备状态的一些回调
CBPeripheralDelegate:外设代理,负责对外设的一些 *** 作,特征的订阅,以及设备信息和消息的更新回调
搜索&连接
连接成功&失败
搜索到服务
搜索到特征
收到外设消息更新
断开设备连接
接下来介绍OTA升级
OTA是DFU(Device Firmware Update)的一种类型,准确说,OTA的全称应该是OTA DFU,就是设备固件升级的意思。只不过大家为了方便起见,直接用OTA来指代固件空中升级(有时候大家也将OTA称为FOTA)。
OTA升级并不复杂,只需要按照硬件定制的协议,把数据按照正常的写入方式发送给硬件即可(注意查看硬件是否规定数据的大小端),如果遇到问题可以找我,可以一起讨论。
16进制类型的字符串[A-F,0-9]和Data之间的转换可以使用下面的方法。如果是包含=之类的可以直接用字符串转换Data即可
以上就是关于ios OC、swift混编制作framework全部的内容,包括:ios OC、swift混编制作framework、swift 怎么实文件读写、swift 蓝牙开发、OTA升级等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)