几乎没有人愿意在收益甚微的情况下把 Objective-C 代码重写一次,因为重写意味着大量的研发资源和测试资源的消耗,而这基本不会产生实际意义上的产品收益。另外 Apple 在 Swift 和 Objective-C 的桥接上做了相当多的努力,大部分情况下,我们都可以不太费力的在 Swift 中复用 Objective-C 代码。
所以大部分公司的策略都会是新功能用 Swift 语言实现,旧功能先维持现状,等到产品变动或是架构变动的时候再使用 Swift 语言实现,这也是猿题库所采用的策略。所以我想短时间内不太可能完全迁移到 Swift 吧。如果非要说一个时间的话,我推测大约一年之后大部分业务应该会迁移到 Swift,而整个项目彻底迁移恐怕就需要两到三年以上了。
从 Objective-C 迁移到 Swift 代表着在很长一段时间内都是处于混编的状态,这里我认为最重要的经验是一定要有一个迁移的规范和指南来约束开发团队哪些部分需要迁移 Swift 而哪些部分不要,接口应该如何设计才能兼顾混编调用。举个例子,由于 Swift 调用 Objective-C 是方便的可靠的,反之则有许多的限制,所以设计接口的时候,需要考虑这个模块会不会被 Objective-C 调用,如果是的话,应该避免使用 Swift 语言的特性,又或是制定一套规则,来 Swift 语言的接口如何转成 Objective-C 兼容的接口。
重上面的总结可以看出,由于 swift 语法还没有稳定,项目中使用 swift也应避免 swift 新特性;本质是 OC 代码转 swift;但是我们可以学习 swift 语法,将来 swift 稳定后更好的上手;
混编注意事项在 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这个宏,如果要实现相同的需求
设置预编译命令
预编译
在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调用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工程中自动提示是否创建桥接头文件
注意:可能方法名 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 文件的. 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
}()
一般是使用系统推荐的,没有自己强行去设置;不是很熟建议使用系统的;
扩展和 Objective-C 中的分类(categories)类似。(不过与Objective-C不同的是,Swift 的扩展没有名字。)
Swift 中的扩展可以:
注意:
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
https://numbbbbb.gitbooks.io/-the-swift-programming-language-/content/chapter2/20_Extensions.html
delegate
想要在 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
默认是 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
}
可选类型(option),不用去判断
Swift 中如何实现Object-C的respondsToSelector
notification
notification 的用法 swift与 OC 保持一致,写法上有些不一样NotificationCenter issue on Swift 3
通知名:
Notification.Name
结构体类型swift中通知的推荐写法
// Definition:
extension Notification.Name {
static let yourCustomNotificatiOnName= Notification.Name("yourCustomNotificationName")
}
// Usage:
NotificationCenter.default.post(name: .yourCustomNotificationName, object: nil)
closure(闭包)
闭包和OC中的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 var weakSelf = self
let closure = { [weak self] in
self?.doSomething() //记住,所有的weak变量都是可选类型。
}
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 全局变量,需要 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等错误