热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

swift和OC混编的一些tips

前言Objective-C代码重写成swift可能性不大,往后功能用swift写几乎没有人愿意在收益甚微的情况下把Objective-C代码重写一次,因为重写意味着大量的研发资源和
前言
  • Objective-C 代码重写成 swift 可能性不大,往后功能用 swift 写

几乎没有人愿意在收益甚微的情况下把 Objective-C 代码重写一次,因为重写意味着大量的研发资源和测试资源的消耗,而这基本不会产生实际意义上的产品收益。另外 Apple 在 Swift 和 Objective-C 的桥接上做了相当多的努力,大部分情况下,我们都可以不太费力的在 Swift 中复用 Objective-C 代码。

所以大部分公司的策略都会是新功能用 Swift 语言实现,旧功能先维持现状,等到产品变动或是架构变动的时候再使用 Swift 语言实现,这也是猿题库所采用的策略。所以我想短时间内不太可能完全迁移到 Swift 吧。如果非要说一个时间的话,我推测大约一年之后大部分业务应该会迁移到 Swift,而整个项目彻底迁移恐怕就需要两到三年以上了。

  • swift 目前不是很稳定,以OC 语法为基准,避免使用 Swift 语言的特性

从 Objective-C 迁移到 Swift 代表着在很长一段时间内都是处于混编的状态,这里我认为最重要的经验是一定要有一个迁移的规范和指南来约束开发团队哪些部分需要迁移 Swift 而哪些部分不要,接口应该如何设计才能兼顾混编调用。举个例子,由于 Swift 调用 Objective-C 是方便的可靠的,反之则有许多的限制,所以设计接口的时候,需要考虑这个模块会不会被 Objective-C 调用,如果是的话,应该避免使用 Swift 语言的特性,又或是制定一套规则,来 Swift 语言的接口如何转成 Objective-C 兼容的接口。

  • 使用 swift 是一个过渡,熟悉 swift 语法的过程

重上面的总结可以看出,由于 swift 语法还没有稳定,项目中使用 swift也应避免 swift 新特性;本质是 OC 代码转 swift;但是我们可以学习 swift 语法,将来 swift 稳定后更好的上手;

混编注意事项

@objc

在 Swift 代码中,使用@objc修饰后的类型,可以直接供 Objective-C 调用。可以使用@objc修饰的类型包括:

  • 未嵌套的类
  • 协议
  • 非泛型枚举(仅限于原始值为整形的类型)
  • 类和协议中的属性和方法
  • 构造器和析构器
  • 下标

每周 Swift 社区问答:@objc

对象方法类方法

与类方法比较,对象方法需要在前面加了一个class
The Swift Programming Language (Swift 3.0.1)-Methods

swift中是不能使用宏定义语法的,但是因为命名空间的缘故,我们可以给我们的项目添加一个空的Const.swift文件,在其中,我们将原本oc中不需要接受参数的宏,定义成let常量,将需要接受参数的宏定义成函数即可,由于我们的整个项目共享命名空间,我们就可以在项目内的任何地方直接使用Const.swift中定义的这些公共的常量和函数

1.没有参数的宏

//oc中的宏定义
#define kIOS7 [UIDevice currentDevice].systemVersion.doubleValue>=7.0 ? 1 :0
#define kIOS8 [UIDevice currentDevice].systemVersion.doubleValue>=8.0 ? 1 :0
#define kScreenHeight [UIScreen mainScreen].bounds.size.height
#define kScreenWidth [UIScreen mainScreen].bounds.size.width
//转换成swift的写法
let kIOS7 = (UIDevice().systemVersion as NSString).doubleValue >= 7.0 ? 1 :0
let kIOS8 = (UIDevice().systemVersion as NSString).doubleValue >= 8.0 ? 1 :0
let kScreenHeight = UIScreen.main.bounds.size.height
let kScreenWidth = UIScreen.main.bounds.size.width

2、接收参数的宏

//oc写法
#define RGBCOLOR(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
//swift中的写法
func RGBCOLOR(r:CGFloat,_ g:CGFloat,_ b:CGFloat) -> UIColor
{
return UIColor(red: (r)/255.0, green: (g)/255.0, blue: (b)/255.0, alpha: 1.0)
}

在 swift 中导入 OC 宏文件是没有用的,因为不能识别;例如kScreenWidth只能识别

let kScreenWidth = UIScreen.main.bounds.size.width

预编译命令

由于Swift没有预处理,正如Apple文档中所说:The Swift compiler does not include a preprocessor.
对于C或者OC中用来定义常量的宏,在Swift中可以使用全局的常量来代替。
Swift是根据构建配置来进行条件编译的。在需要区分开发还是发布版本的时候,OC这样做

#ifdef DEBUG
#else
#endif

但是在swift中没有DEBUG这个宏,如果要实现相同的需求

《swift 和 OC 混编的一些 tips》 设置预编译命令

预编译

在target下 Build Settings 搜索 Other [Swift](http://lib.csdn.net/base/swift) Flags
设置Debug 添加 -D DEBUG,注意不要好Release一起添加
使用方式和oc下一样
#if ABC // 判断是否在[测试]环境下
// TODO
#else
// TODO
#endif

OC->Swift advanced tips

swift 中使用 OC类

Swift调用Objective-C文件比较简单。当在Swift工程中新建Objective-C文件或者在Objective-C工程中新建Swift文件时,Xcode会自动提示你是否创建bridging header桥接头文件,点击创建后Xcode会自动为你创建一个桥接头文件。如图1-1、1-2所示,在基于Swift的SwiftProject工程中创建一个OCViewController时,Xcode会自动创建一个名为SwiftProject-Bridging-Header.h桥接头文件。

《swift 和 OC 混编的一些 tips》 Swift工程中自动提示是否创建桥接头文件

注意:可能方法名 swift 会帮你改变些

OC 中使用 swift 类

Xcode会自动为Project生成头文件以便在Objective-C中调用。在Objective-C类中调用Swift,只需要#import “productModuleName-Swift.h”即可调用,Xcode提供的头文件以Swift代码的模块名加上-Swift.h为命名。在这个头文件中,将包含Swift提供给Objective-C的所有接口、Appdelegate及自动生成的一些宏定义,如图1-5所示。注意productModuleName-Swift.h在Xcode中是无法搜索查看的,只能从import中点击进去查看。

《swift 和 OC 混编的一些 tips》 查看 swift 文件的. h 头文件

枚举

在 OC 中的枚举建议使用 typedef 这种格式, swift 会自动帮忙转换,其他的枚举格式转换不了

typedef NS_ENUM(NSInteger, QDMenuItemType) {
QDMenuItemTypePlaceHolder = 0,
QDMenuItemTypeCredit = 1,
QDMenuItemTypeOrder = 2,
QDMenuItemTypeGiftCard = 3,
QDMenuItemTypeFocus = 4,
QDMenuItemTypeAddress = 5,
QDMenuItemTypeCooperation = 6,
QDMenuItemTypeWX = 7,
QDMenuItemTypeWXLine = 8,
QDMenuItemTypeQQ = 9,
};
override func handleMenuItemClick(_ type: QDMenuItemType) {

var index = 0;

switch type {
case QDMenuItemType.WX:
index = 0;
break;
case QDMenuItemType.wxLine:
index = 1;
break;
case QDMenuItemType.QQ:
index = 2;
break;
default:

break;
}

let urlStr = "https://htest.qianduan.com/app/download";
let cOntentMsg= "预约投资轻松理财,随时随地享收益,想赚请戳我";
let projectName = "投资达人都该拥有!";

self.share(with: index, url: urlStr, contentMsg: contentMsg, title: projectName, thumImage: nil);
}

How to use Objective-C enum in Swift [duplicate]

懒加载

它本质在siwft中确实是一个闭包,执行顺序是这样的,如果这个lazy修饰的变量没值,就会执行闭包中的东西,不是每次都执行(本人补充:这也就是为什么在Swift中的懒加载没有oc中判断。if(xx==nil){初始化xx}的代码段)。

懒加载有两种调用方式:
1.带有Lazy关键字修饰的闭包方式,这种只有在使用的时候才会调用,Lazy是延迟调用关键字.
2.只使用闭包的方式,这种方式没有延迟调用的特性,在持有它的对象初始化的时候就会调用,就像上面的代码label2.

lazy var label1: UILabel = {//CGRectMake(30, 30, 200, 50)
let label1 = UILabel.init(frame:CGRect(x: 30, y: 30, width: 200, height: 50))
label1.text = "lazy方式懒加载lable1"
label1.textColor = UIColor.white
label1.backgroundColor = UIColor.cyan

return label1
}() private var label2 : UILabel = {
let label2 = UILabel.init(frame:CGRect(x: 30, y: 30, width: 200, height: 50))
label2.text = "闭包方式懒加载lable1"
label2.textColor = UIColor.white
label2.backgroundColor = UIColor.red

return label2
}()

optional 关键字

一般是使用系统推荐的,没有自己强行去设置;不是很熟建议使用系统的;

extension 的用法

扩展和 Objective-C 中的分类(categories)类似。(不过与Objective-C不同的是,Swift 的扩展没有名字。)

Swift 中的扩展可以:

  • 添加计算型属性和计算静态属性
  • 定义实例方法和类型方法
  • 提供新的构造器
  • 定义下标
  • 定义和使用新的嵌套类型
  • 使一个已有类型符合某个协议

注意:
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。

https://numbbbbb.gitbooks.io/-the-swift-programming-language-/content/chapter2/20_Extensions.html

delegate notification closure

delegate

  • weak引用的问题

想要在 Swift 中使用 weak delegate,我们就需要将 protocol 限制在 class 内。一种做法是将 protocol 声明为 Objective-C 的,这可以通过在 protocol 前面加上 @objc 关键字来达到,Objective-C 的 protocol 都只有类能实现,因此使用 weak 来修饰就合理了:

@objc protocol MyClassDelegate {
func method()
}

另一种可能更好的办法是在 protocol 声明的名字后面加上 class,这可以为编译器显式地指明这个 protocol 只能由 class 来实现。

protocol MyClassDelegate: class {
func method()
}

相比起添加 @objc,后一种方法更能表现出问题的实质,同时也避免了过多的不必要的 Objective-C 兼容,可以说是一种更好的解决方式。

DELEGATE

@OBJC 和 DYNAMIC

  • option关键字

默认是 require,如果想可选 前面加option

public protocol UITableViewDataSource : NSObjectProtocol { @available(iOS 2.0, *)
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int @available(iOS 2.0, *)
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell @available(iOS 2.0, *)
optional public func numberOfSections(in tableView: UITableView) -> Int // Default is 1 if not implemented
}

  • responseToSeletor

可选类型(option),不用去判断

Swift 中如何实现Object-C的respondsToSelector

notification

notification 的用法 swift与 OC 保持一致,写法上有些不一样NotificationCenter issue on Swift 3

通知名:

  • OC中为 NSString 类型
  • swift变成Notification.Name结构体类型

swift中通知的推荐写法

// Definition:
extension Notification.Name {
static let yourCustomNotificatiOnName= Notification.Name("yourCustomNotificationName")
}
// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)

closure(闭包)

闭包和OC中的block非常相似

  • OC中的block是匿名的函数
  • Swift中的闭包是一个特殊的函数
  • block和闭包都经常用于回调


let request = QDAppGlobalConfigsRequestDto()
QDIndexService().request(request, sucessBlock: { (response) in

if let result = response as? QDAppGlobalConfigsResponse{
print(result)
}

}, fail: { (error) in

QDShowErrorTool.showCommonErrorMsg(error, allowShow: true);

})

循环引用:

  • 使用weak修饰变量, 打破强引用, 因为使用weak修饰的变量有一次变成nil的机会

weak var weakSelf = self

  • 使用[weak self] 修饰闭包原理跟__weak类似, 这样在闭包中使用self, 就是弱引用

let closure = { [weak self] in
self?.doSomething() //记住,所有的weak变量都是可选类型。
}

  • 使用[unowned self] 修饰闭包, 跟__unsafe_unretained类似, 不安全

closure = { [unowned self] in
self.string = "Hello, World!"
}

解析Swift中闭包的循环引用

https://numbbbbb.gitbooks.io/-the-swift-programming-language-/content/chapter2/21_Protocols.html

命名空间

swift 和 OC的 class 名不能一样,否者会重复命名的错误

/Users/chenbowen/Library/Developer/Xcode/DerivedData/OC2SwiftDemo-bhnddbvgrdbyriarpwmbreemuhdm/Build/Intermediates/OC2SwiftDemo.build/Debug-iphonesimulator/OC2SwiftDemo.build/DerivedSources/OC2SwiftDemo-Swift.h:136:1: Duplicate interface definition for class 'EnumTestViewController'

OC 中获取不能直接通过 swift类名来获取类,需要加上工程名;否者会报错

NSString *classStr = self.listArr[indexPath.row];
Class vcClass = NSClassFromString(classStr);//命名空间的问题
swift 中的类

Application tried to push a nil view controller on target .

swift 可以允许同名的类出现,但是需要需要放在不同的空间中,具体可以可以王巍文档命名空间

swift 和 OC 的全局变量

  • swift中使用 OC 全局变量,需要 import 头文件就可以

  • OC 想使用 swift 定义的全局变量;需要swift 提供一个方法
    Swift globals in objective c

  • swift 中定义全局变量,放在类外面就可以;swift的全局变量不能重名,如果两个重名都会报错

模拟器可以运行,真机运行报错

真机调试APP时报dyld: Library not loaded: @rpath/XXX等错误

XCode真机调试APP时报dyld: Library not loaded: @rpath/XXX等错误

参考文档

猿题库从 Objective-C 到 Swift 的迁移
每周 Swift 社区问答:@objc
The Swift Programming Language (Swift 3.0.1)-Methods
OC->Swift advanced tips(1)
How to use Objective-C enum in Swift [duplicate]
DELEGATE
@OBJC 和 DYNAMIC
Swift 中如何实现Object-C的respondsToSelector
NotificationCenter issue on Swift 3
解析Swift中闭包的循环引用
命名空间
Swift globals in objective c
XCode真机调试APP时报dyld: Library not loaded: @rpath/XXX等错误


推荐阅读
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • XNA 3.0 游戏编程:从 XML 文件加载数据
    本文介绍如何在 XNA 3.0 游戏项目中从 XML 文件加载数据。我们将探讨如何将 XML 数据序列化为二进制文件,并通过内容管道加载到游戏中。此外,还会涉及自定义类型读取器和写入器的实现。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • CMake跨平台开发实践
    本文介绍如何使用CMake支持不同平台的代码编译。通过一个简单的示例,我们将展示如何编写CMakeLists.txt以适应Linux和Windows平台,并实现跨平台的函数调用。 ... [详细]
  • 本文详细介绍了如何构建一个高效的UI管理系统,集中处理UI页面的打开、关闭、层级管理和页面跳转等问题。通过UIManager统一管理外部切换逻辑,实现功能逻辑分散化和代码复用,支持多人协作开发。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文介绍如何使用阿里云的fastjson库解析包含时间戳、IP地址和参数等信息的JSON格式文本,并进行数据处理和保存。 ... [详细]
  • 深入理解Java泛型:JDK 5的新特性
    本文详细介绍了Java泛型的概念及其在JDK 5中的应用,通过具体代码示例解释了泛型的引入、作用和优势。同时,探讨了泛型类、泛型方法和泛型接口的实现,并深入讲解了通配符的使用。 ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • 本文详细介绍了如何解决Uploadify插件在Internet Explorer(IE)9和10版本中遇到的点击失效及JQuery运行时错误问题。通过修改相关JavaScript代码,确保上传功能在不同浏览器环境中的一致性和稳定性。 ... [详细]
  • 本文将介绍如何使用 Go 语言编写和运行一个简单的“Hello, World!”程序。内容涵盖开发环境配置、代码结构解析及执行步骤。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
author-avatar
nikechen
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有