从OC到Swift(一)
开篇
在简单的学习了swift语法之后,开始在项目里使用。很多东西都是别人铺好路,自己只是套用写UI而已。这里先简单过渡一下OC-Swift,后续再深入研究Swift相关知识。
Swift与OC如何互相调用
大部分项目应该都是要从OC过渡到Swift,这就避免不了混编,这里涉及到两个桥接头文件:
1. Swift调用OC {targetName}-Bridging-Header.h
作用:Swift可调用OC,在此文件中import OC的头文件
在OC工程中创建swift文件时会有此提示,点击create Xcode会帮我们生产此文件。
image.png并且在
Targets-->Build Settings-->Swift Compiler - General-->Objective-C Bridging Header
有配置路径。如果工程里没有此文件,我们可以手动创建,并且在这里配置一下,不过注意的是文件名字必须为上述格式{targetName}-Bridging-Header.h
image.png
2. OC调用Swift 一般为{targetName}-swift.h
在上面图中我们看到Objective-C Generated Interface Header Name
这里也有一个配置的文件路径,这里就是让OC可与调用Swift。需要调用swift的地方,import这个文件就可以。
名字为什们说一般为{targetName}-swift.h
呢?当targetName中存在-
这种情况,需要变_
。 例如targetNameOC-Swift
,这个头文件的名字应该是OC_Swift-Swift.h
。这个文件内是swift编译生成的对应OC代码。
这个文件在工程内不能直接看到,编译后,可以通过Command+单击该文件名,就会看到具体生成的代码。
需要注意的是:
- 在OC里如果想要调用Swift,需要在被访问的类,属性,方法前面加上@objc,并且如果是类,需要继承NSObject,否则编译也会报错。
@objc class TestSwiftClass: NSObject { }
- 这就又有问题了,如果一个类特别复杂都需要暴露给OC,那不是得写好多@objc?这时候可以使用@objcMembers, 这样类、类中的所有属性、方法都暴露给OC可以直接调用。
@objcMembers class Car: NSObject { }
- 我们还可以给swift属性、方法重新定义供OC使用的名字
@objc(realName)
var name: String;
@objc(ocRun)
func run() {
}
以上整体下来示例:
//swift中写法
@objcMembers class TestSwiftClass: NSObject {
@objc(realName)
var name: String;
var age: Int;
init(name:String, age:Int) {
self.name = name
self.age = age
}
@objc(ocRun)
func run() {
}
}
//OC中调用
- (void)viewDidLoad {
[super viewDidLoad];
TestSwiftClass *obj = [[TestSwiftClass alloc]initWithName:@"zhangsan" age:12];
obj.age = 11;
obj.realName = @"ZhangSan";
[obj ocRun];
}
- 我们上面会发现swif中定义的init方法
init(name:String, age:Int)
,而OC里面调用还是我们熟悉的样子TestSwiftClass *obj = [[TestSwiftClass alloc]initWithName:@"zhangsan" age:12];
而且OC里面的方法- (instancetype)initWithTitle:(NSString *)title;
到Swift中调用的时候是let obj = TestOCClass(title: "title")
这里是xcode帮我做了优化,生成对应风格的代码。
这里有个坑,就是你可能会发现在swift中无法调用一些OC的单例创建方法,比如 + (instancetype)manager, 或者+(instancetype)shareManager。后来发现需要定义一些标准名字比如default singleton shared 。不过这个问题应该在只存在某几个版本的Xcode中,目前发现Xcode11.3没有这个问题
以上大概就已经做好混编的基础了。
Selector的使用
在OC里面我们经常使用@selector(name)
,在swift里面只是把@替换#,例:#selector(name)
。
- 注意的是,必须是被@objcMembers或@objc修饰的方法才可以定义选择器。
为什么呢?我们可以想一下@selector(name)是依赖OC的runtime,纯swift是没有runtime的,所以既然要使用方法选择器,说明需要用到runtime,所以需要通过关键字桥接到OC。当然在swift里面也可以调用perform(#selector(name))
String的区别
我们发现swift中的String在OC中会自动转换为NSString。但是Swift中的String的用法确与OC相差很大。在系统学习之前,我两个月都没用明白(但是不能承认自己笨。嗯!咳)。
关于字符串拼接之类的就不多说了,主要说一下关于String的Index。
String的下标(Index)并不是int类型,而是String.Index,Index是个结构体
image.png
虽然他不是整形,但是他一样代表着位置。关于String类型的一些方法,.startIndex/.endIndex!,例:name.startIndex/name.endIndex
代表是起始位置(第一个字符的位置)/最后位置(最后一个字符的下一位)
关于String的一些插入删除方法:
//插入
var str = "a-b"
// 插入单个字符,最后一个位置插入‘-’,结果:a-b-
str.insert("-", at: str.endIndex)
// 插入字符串使用contentsOf:末尾插入c-d,结果:a-b-c-d
str.insert(contentsOf: "c-d", at: str.endIndex)
// after:str.startIndex,在第一个字符的后一位,结果:a666-b-c-d
str.insert(contentsOf: "666", at: str.index(after: str.startIndex))
// before:str.endIndex,在末尾的前一位,也就是最后一个字符的位置插入,从插入位置起,原字符都顺位后移,结果:a666-b-c-888d
str.insert(contentsOf: "888", at: str.index(before: str.endIndex))
// offsetBy:可以指定偏移量,从开始向后偏移3,也就是第三个6的位置,插入后,原字符顺位后移,结果 a66hello6-b-c-888d
str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 3))
//删除
//删除第一个'-' 结果:a66hello6b-c-888d
str.remove(at: str.firstIndex(of: "-")!)
//删除所有,符合闭包条件的所有值。结果ahellob-c-888d
str.removeAll { $0 == "6" }
还有其他的关于range的方法,就不在这罗列了,明白了上面这些关于String.Index的使用,其他的基本一样了。
Substring
String可以通过下标、 prefix、 suffix等截取子串,但是子串类型不是String,而是Substring。String本身就是个结构体,不能被继承。Substring也就不是什么子类之类的。他一样也是个结构体。
- Substring 是字符串的一部分,和父字符串共享同一块内存空间,并且记录了自己的开始和结束位置。
- String 和 Substring 都声明实现了 StringProtocol。StringProtocol 包含了一个字符串的基本属性和功能。
- Substring发生修改 或者 转为String时,会分配新的内存存储字符串数据
具体细节,大家自行Google吧。