优雅的开发Swift和Objective-C混编的Framework

简介:

前言

为什么要写这样一篇文章,因为昨天和一个朋友讨论到Swift和Objective C如何混合开发Framework,中途发现了很多有意思的坑。

用Swift封装OC的库是一件比较常见的事情,毕竟对于大多数公司来说,老的代码都是用OC写的,而且经过多次迭代,这些OC的代码已经被验证了是稳定的,用Swift重写代价太大。这就引入了一个需求:

  • 用Swift和OC来混编一个Framework。

如果你之前没有用Swift和Objective C混合开发,建议看看这篇文档:

  • Swift and Objective-C in the Same Project

https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

按照文档一步一步来

新建一个基于单页面工程,然后新建一个一个Target,选中Cocoa Touch Framework。然后,分别新建一个Swift文件和Objective C类,注意Target Member Ship选中Framework。类的内容如下:

OCSource.h

 
 
  1. #import <span class="hljs-title"><Foundation/Foundation.h></span> 
  2.  
  3.   
  4.  
  5. @interface OCSource : NSObject 
  6.  
  7. - (void)functionFromOC; 
  8.  
  9. @end  

OCSource.m

 
 
  1. #import "OCSource.h" 
  2.  
  3.   
  4.  
  5. @implementation OCSource 
  6.  
  7.   
  8.  
  9. - (void)functionFromOC{ 
  10.  
  11.     NSLog(@"%@",@"Log from objective c in framework"); 
  12.  
  13.  
  14. @end  

Swift调用OC

新建SwiftSource.swift

 
 
  1. open class SwiftIt{ 
  2.  
  3.     public init(){} 
  4.  
  5.     let ocObject = OCSource() 
  6.  
  7.     public func encapsulate(){ 
  8.  
  9.         ocObject.functionFromOC() 
  10.  
  11.     } 
  12.  
  13.  

然后,按照文档中,为了让Swift文件访问Objective C文件,我们应该在umbrella header,也就是MixFramework.h中,暴露所需要的header。

也就是,MixFramework.h,

 
 
  1. #import <MixFramework/OCSource.h> 

然后,自信满满的点击build。

Boom~~~,编译不通过。

原因:OCSource.h默认编译的时候是Project权限. 为了在umbrella header中使用,要把这个文件的权限改成Public

按照图中的方式拖过去即可。

嗯,现在build,可以看到build成功了。

OC调用Swift

在SwiftSource.swift中,增加一个类,

 
 
  1. open class ClassForOC:NSObject{ 
  2.  
  3.     public static let textForOC = "textForOC" 
  4.  
  5.  

然后,为了在OC中调用Swift的方法,我们需要导入头文件,这时候,OCSource.m文件内容如下

 
 
  1. #import "OCSource.h" 
  2.  
  3. #import <MixFramework/MixFramework-Swift.h> 
  4.  
  5.   
  6.  
  7. @implementation OCSource 
  8.  
  9.   
  10.  
  11. - (void)functionFromOC{ 
  12.  
  13.     NSLog(@"%@",[ClassForOC textForOC]); 
  14.  
  15.  
  16. @end  

然后,build,发现成功了,很开心。

外部调用

在ViewController.swift中,我们调用Framework中的内容。

 
 
  1. import MixFramework 
  2.  
  3. class ViewController: UIViewController { 
  4.  
  5.     var t = SwiftIt() 
  6.  
  7.     override func viewDidLoad() { 
  8.  
  9.         super.viewDidLoad() 
  10.  
  11.         t.encapsulate() 
  12.  
  13.         // Do any additional setup after loading the view, typically from a nib. 
  14.  
  15.     } 
  16.  
  17.   
  18.  
  19.     override func didReceiveMemoryWarning() { 
  20.  
  21.         super.didReceiveMemoryWarning() 
  22.  
  23.         // Dispose of any resources that can be recreated. 
  24.  
  25.     } 
  26.  
  27.  

然后运行,发现控制台打印出

 
 
  1. 2017-03-02 16:08:24.000 HostApplication[19524:167669] textForOC 

嗯,framework打包成功了。

问题

通常,我们希望暴露给外部的接口是纯Swift,而OC文件的具体接口应该隐藏,这就是我标题中的优雅两个字的含义。

如果你好奇,你会发现,在ViewController.swift中你可以这么调用

 
 
  1. var s = OCSource() 

也就是说,OC的内容也暴露出来了,这破坏了Framework的封装特性。

通过查看MixFramework的编译结果,发现最后暴露出的接口是这样子的

 
 
  1. import Foundation 
  2.  
  3. import MixFramework.OCSource 
  4.  
  5. import MixFramework 
  6.  
  7. import MixFramework.Swift 
  8.  
  9. import SwiftOnoneSupport 
  10.  
  11. import UIKit 
  12.  
  13.   
  14.  
  15. // 
  16.  
  17. //  MixFramework.h 
  18.  
  19. //  MixFramework 
  20.  
  21. // 
  22.  
  23. //  Created by Leo on 2017/3/2. 
  24.  
  25. //  Copyright © 2017年 Leo Huang. All rights reserved. 
  26.  
  27. // 
  28.  
  29.   
  30.  
  31. //! Project version number for MixFramework. 
  32.  
  33. public var MixFrameworkVersionNumber: Double 
  34.  
  35. open class ClassForOC : NSObject { 
  36.  
  37.   
  38.  
  39.     public static let textForOC: String 
  40.  
  41.  
  42.   
  43.  
  44. open class SwiftIt { 
  45.  
  46.   
  47.  
  48.     public init() 
  49.  
  50.   
  51.  
  52.     public func encapsulate() 
  53.  
  54.  

这一行,把OC对应的实现暴露出来了

 
 
  1. import MixFramework.OCSource 

优雅的解决方案

不再通过umbrella header的方式让framework中的Swift调用OC方法。而是通过modulemap。

新建一个module.modulemap文件,内容如下

 
 
  1. module OCSource [system] { 
  2.  
  3.     //由于module.modulemap和OCSource.h是在同一个文件夹的,如果不是同一个,路径要写全 
  4.  
  5.     header "OCSource.h" 
  6.  
  7.     export * 
  8.  
  9.   

这里的#(SRCROOT)是XCode的宏,会自动替换成项目所在的根目录,这里输入的路径是module.modulemap文件所在的路径。

然后,删除MixFramework.h(umbrella header)中#import 的OC header。

把OCSource.h的权限改回默认的project。

再编译,发现OC的类被隐藏了。

总结

如果你要开发一个framework,一定要想清楚哪些接口暴露出去,哪些封装起来,framework不是简单把一包文件加个壳子。




本文作者:佚名
来源:51CTO
目录
相关文章
|
3天前
|
存储 Swift 索引
Swift开发——索引器扩展
扩展用于向已存在的类型(例如,类、结构体、枚举和协议等)中添加新的功能,扩展甚至可以向系统类型(包括无法查阅代码的类型)中添加新的功能,但是扩展不能覆盖原类型中已有的方法,扩展也不能向类中添加新的存储属性。
26 6
Swift开发——索引器扩展
|
3天前
|
存储 Swift
Swift开发——属性检查器
Swift中的属性检查器(willSet, didSet)允许在设置存储属性值前后执行代码。在类`Circle`中,属性`radius`使用属性观察器:willSet在赋值前检查值,若值为负则打印警告;didSet在赋值后比较新旧值,根据变化输出相应信息。在实例`c`中,`radius`从-5变为0时,输出“Input value is negative.”和“The circle gets smaller.”;从0变为10时,输出“Input value is normal.”和“The circle gets larger.”。
8 4
Swift开发——属性检查器
|
5天前
|
Swift C++ 索引
Swift开发——简单函数实例
函数是编程的基础,用于封装特定功能的代码。它们有关键词func、函数名、参数列表(可为空)和返回类型。多返回值可通过元组、数组、inout参数或可选类型实现。例如,返回元组 `(value1, value2)`,数组 `[value1, value2]` 或使用可选数组 `[[Double]]?`。函数可以作为其他函数的参数,类似闭包或Lambda表达式。在Swift中,示例展示了通过元组、带索引的元组、数组和可选类型返回多个值的函数。还演示了如何使用inout参数交换变量值。
21 5
Swift开发——简单函数实例
|
2天前
|
存储 Swift
Swift开发——弱占用
Swift的自动引用计数(ARC)管理类实例内存,通过强引用保持实例存活。当出现强引用循环时,可使用`weak`关键字创建弱引用,避免阻止实例释放。弱引用在不再被强引用时导致对象立即释放。示例中,添加`weak`至`author`和`book`变量防止引用循环,使得两者析构器均执行,释放内存。图2展示了弱引用结构,当解除所有强引用后,ARC自动释放实例,调用析构器。
11 1
Swift开发——弱占用
|
4天前
|
存储 程序员 Swift
Swift开发——存储属性与计算属性
Swift推荐使用结构体进行开发,结构体支持属性和方法,且作为值类型。结构体属性包括存储属性(如radius)和计算属性(如r),计算属性不存储值,类似方法。结构体用`struct`定义,命名遵循大驼峰规则。实例名遵循小驼峰规则。属性可在结构体中任意位置定义,静态属性用`static`。存储属性可为`lazy`实现懒加载。结构体实例通过`.`访问属性和方法,静态属性和方法用`结构体名.`访问。计算属性可读写,可通过`get`和`set`定义。程序段1展示了结构体Point和Circle的属性和方法,包括私有属性、只读计算属性、可读写计算属性及`mutating`方法。
6 0
Swift开发——存储属性与计算属性
|
6天前
|
存储 算法 Swift
Swift开发——循环执行方式
Swift语言中的循环主要包括`for-in`和`while`结构。`for-in`适用于遍历数字区间、字符串和字典,支持使用`stride`函数定制步进。字典遍历时,可以用二元元组`(k, v)`访问键值对。`while`循环有标准形式和`repeat-while`形式,确保至少执行一次循环体。程序示例展示了`for-in`和不同`while`结构的用法,包括计算阶乘、奇数和、加密字符串以及最大公约数和最小公倍数。
9 0
Swift开发——循环执行方式
|
7天前
|
Swift 索引
Swift开发——元组
Swift中的元组是一种数据结构,用于组合不同类型的值。它们不是独立的数据类型,而是以有序序列形式存在,用圆括号括起,元素间用逗号分隔。元组可以有任意数量和类型的元素,可变性取决于其定义。常用于函数返回多个值。示例代码展示了元组的创建、访问、解包及赋值。元组可以通过标签来标识元素,支持嵌套和比较。在函数返回值和并行赋值场景中,元组特别有用。
22 0
Swift开发——元组
|
1月前
|
安全 编译器 Swift
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
139 2
|
1月前
|
缓存 开发工具 iOS开发
优化iOS中Objective-C代码调起支付流程的速度
优化iOS中Objective-C代码调起支付流程的速度
24 2
|
1月前
|
安全 JavaScript 前端开发
IOS开发基础知识:介绍一下 Swift 和 Objective-C,它们之间有什么区别?
IOS开发基础知识:介绍一下 Swift 和 Objective-C,它们之间有什么区别?
103 0