1、Swift 调 OC
在桥接文件 SwiftDemo-Bridging-Header.h 里导入,如:
#import
#import "TestView.h"
#import "UIView+HUD.h"
但需要注意的是,OC 的方法经过系统自动转换为 Swift 代码后,方法名可能会发生变化,比如单例方法:
+ (instancetype)sharedModel;
在 Swift 里的调用为:
UserModel.shared()
2、OC 调 Swift
导入 项目名-Swift.h,这是一个隐式的文件,这里面有系统自动转换的 Swift 代码。
#import "SwiftDemo-Swift.h"
注意要标注了 @objc 的属性和方法,才会被系统自动转换成 OC 的代码。比如:
Swift 属性:
@objc var urlStr: String?
系统自动转换成的 OC 属性:
@property (nonatomic, copy) NSString * _Nullable urlStr;
Swift 方法:
@objc public func cancelAllRequest() {}
系统自动转换成的 OC 方法:
- (void)cancelAllRequest;
3、Swift中的宏
Swift 里没有宏的这个概念,可以使用全局常量、全局变量、全局方法来代替。比如:
OC 的宏
#define kAppWidth UIScreen.mainScreen.bounds.size.width
Swift 的全局常量
let kAppWidth = UIScreen.main.bounds.size.width
OC 的宏
#define kImageNamed(NAME) [UIImage imageNamed:NAME]
Swift 的全局方法
func kImageNamed(_ name: String) -> UIImage? {
return UIImage.imageNamed(name)
}
4、分类的处理
Swift 的分类的应用比 OC 多,在一个 Swift 的类里,经常使用一个分类来实现某个功能模块,比如:
// MARK: - TableView
extension SwiftController: UITableViewDelegate, UITableViewDataSource {}
// MARK: - 点击事件
extension SwiftController {}
给系统类添加方法,比如:
extension Dictionary {
// MARK: 字典转字符串
func stringValue() -> String? {
let data = try? JSONSerialization.data(withJSONObject: self, options: [])
let str = String(data: data!, encoding: String.Encoding.utf8)
return str
}
}
OC 的分类,在桥接文件 SwiftDemo-Bridging-Header.h 里导入后,可以直接调用,比如:
导入头文件
#import "UIImage+Extention.h"
OC 分类的方法声明
@interface UIImage (Extention)
/// 水印图片
- (UIImage *)waterImage;
@end
Swift 调用方法
let waterImg: UIImage = image!.water()
5、一些需要特别注意的语法
语法文档:https://swiftgg.gitbook.io/swift
5.1、类型转换
Swift 中,值永远不会被隐式转换为其他类型,只能显式转换,比如:
let a = 10;
let b = 1.0;
let c = a + Int(b);
let d = Double(a) + b;
let f = String(a)
let g = "\(a)"
5.2、数组字典初始化
let arr0: [Int] = []
let arr1: [Int] = [Int]()
let arr2: [Int] = [Int].init()
let arr3: [Int] = [Int].init(repeating: 0, count: 5)
let dict0: [String: Int] = [:]
let dict1: [String: Int] = [String: Int]()
let dict2: [String: Int] = [String: Int].init()
// 闭包式初始化, 只会执行一次
let arr4: [Int] = { () -> [Int] in
return [1, 2]
}()
// 闭包式初始化, 可省略 () -> [Int] in
let arr5: [Int] = {
return [3, 4]
}()
5.3、循环/遍历
forin 循环:
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13]
]
for (key, numbers) in interestingNumbers {
for number in numbers {
}
for (index, value) in numbers.enumerated() {
}
}
while 循环:
var n = 2
while n < 100 {
n *= 2
}
var m = 2
repeat {
m *= 2
} while m < 100
区间循环:..< 创建的范围不包含上界,如果想包含的话使用 ...
for i in 0..<4 {}
for i in 0...4 {}
let names: [String] = ["a", "b", "c", "d"]
for name in names[2...] {
print(name)
}
for name in names[...2] {
print(name)
}
5.4、解包
if 加 感叹号(!)强制解包:
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
if let 解包:
if let constantName = someOptional {
// someOptional 有值
} else {
// someOptional 为空
}
if let num = Int(optionalNum), let constantName = optionalName, num > 10 {
// optionalNum 有值,optionalName 也有值,且 num 大于10
}
guard let 解包:
guard let name = person["name"] else {
// person["name"] 为空会走到这里
return
}
// person["name"] 有值会继续往下执行
5.5、字符串
String 与 NSString 的无缝桥接:
var str: String = "a"
let nsStr: NSString = str as NSString
str = nsStr as String
多行字符串 ("""):
let quotation = """
The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""
遍历:
for character in "Dog!" {
print(character)
}
for character in [Character]("Dog!") {
print(character)
}
for character in Array("Dog!") {
print(character)
}
字符转字符串:
let catCharacters: [Character] = ["C", "a", "t", "!", ""]
let catString = String(catCharacters)
print(catString)
// 打印输出:“Cat!”
String 的获取索引、插入、删除等操作比较繁琐,常转为 NSString 然后去处理:
let str = "Guten Tag!"
str[str.startIndex] // G
str[str.index(before: str.endIndex)] // !
str[str.index(after: str.startIndex)] // u
let index = str.index(str.startIndex, offsetBy: 7)
str[index] // a
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
let range = welcome.index(welcome.endIndex, offsetBy: -2)..
5.6、值类型和引用类型
Swift 中结构体和枚举是值类型,类(class)是引用类型。
Swift 中所有的基本类型:整数(Int)、浮点数(Float/Double)、布尔值(Bool)、字符串(String)、数组(Array)和字典(Dictionary),都是值类型,其底层也是使用结构体实现的。
值类型在被赋值给一个变量、常量或者被传递给一个函数的时候,传过去的是拷贝后的值。
let str0: String = "a"
var str1 = str0
str1 += "b"
print("str0 = \(str0), str1 = \(str1)")
// str0 = a, str1 = ab
引用类型在被赋予到一个变量、常量或者被传递到一个函数时,传过去的是内存地址。
let nsStr0: NSMutableString = NSMutableString.init(string: "a")
let nsStr1 = nsStr0
nsStr1.append("b")
print("nsStr0 = \(nsStr0), nsStr1 = \(nsStr1)")
// nsStr0 = ab, nsStr1 = ab
5.7、set/get
重写 set/get:
var num: Int {
get {
return 0
}
set(newNum) {
print("\(newNum)")
}
}
简化 Setter 声明,计算属性的 setter 没有定义表示新值的参数名的时候,可以使用默认名称 newValue:
var num: Int {
get {
return 0
}
set {
print("\(newValue)")
}
}
简化 Getter 声明, 在 getter 中忽略 return:
var num: Int {
get {
0
}
set {
print("\(newValue)")
}
}
只读计算属性,只有 getter 没有 setter 的计算属性叫只读计算属性:
// 只读属性 get 的简略写法, 每次都会执行里面的代码
var kTopWindow: UIWindow {
var window = UIApplication.shared.keyWindow!
if #available(iOS 13, *) {
for wScene in UIApplication.shared.connectedScenes where wScene.activationState != UIScene.ActivationState.unattached {
if let windowScene = wScene as? UIWindowScene, windowScene.windows.count > 0 {
window = windowScene.windows.last!
break
}
}
}
return window
}
5.8、类型转换
检查类型, 用类型检查操作符(is)来检查一个实例是否属于特定子类型:
let num = 10
if num is Int {
print(num)
}
类型转换操作符(as? 或 as!):
let num = 10
if let newNum = num as? Int {
print(newNum)
} else {}
let newNum = num as! Int
5.9、弱引用(weak)
weak var weakSelf = self
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
guard let strongSelf = weakSelf else {
return
}
}
5.10、访问控制
open 和 public 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。
通常情况下,可以使用 open 或 public 级别来指定框架的外部接口。
open 只能作用于类和类的成员,它和 public 的区别主要在于 open 限定的类和成员能够在模块外能被继承和重写。
internal 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。
通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 internal 级别, 也是系统默认的访问级别。
fileprivate 限制实体只能在其定义的文件内部访问。
如果功能的部分实现细节只需要在文件内使用时,可以使用 fileprivate 来将其隐藏。
private 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。
如果功能的部分细节只需要在当前作用域内使用时,可以使用 private 来将其隐藏。
5.11、单例
static let sharedInstance: NetWorkSwift = NetWorkSwift()
5.12、GCD
DispatchQueue.main.async {}
DispatchQueue.global().async {}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {}
5.12、闭包
闭包表达式语法:
{ (parameters) -> return type in
statements
}
普通闭包, 顺序执行,不能延时:
private func p_normal(finish: (_ num: Int) -> Void) {
finish(10)
}
逃逸闭包, 可以延时:当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。
private func p_escaping(finish: @escaping (_ num: Int) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
finish(20)
}
print(30)
}
调用
p_escaping { num in
print(num)
}
// 先打印 30,再打印 20
5.13、系统版本判断
if #available(iOS 13, *) {}
5.14、面向协议编程
普通协议:
//定义协议
protocol Shakeable {
func shake()
}
//实现协议
class ShakeView: UIView, Shakeable {
func shake() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.25
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = CGPoint.init(x: self.center.x - 4.0, y: self.center.y)
animation.toValue = CGPoint.init(x: self.center.x + 4.0, y: self.center.y)
self.layer.add(animation, forKey: "position")
}
}
//实现协议
class AnotherShakeView: UIView, Shakeable {
func shake() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.05
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = CGPoint.init(x: self.center.x - 4.0, y: self.center.y)
animation.toValue = CGPoint.init(x: self.center.x + 4.0, y: self.center.y)
self.layer.add(animation, forKey: "position")
}
}
面向协议:
//定义协议
protocol Shakeable {}
//实现协议
extension Shakeable where Self : UIView {
func shake() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.25
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = CGPoint.init(x: self.center.x - 4.0, y: self.center.y)
animation.toValue = CGPoint.init(x: self.center.x + 4.0, y: self.center.y)
layer.add(animation, forKey: "position")
}
}
class ShakeView: UIView, Shakeable {}
class AnotherShakeView: UIView, Shakeable {}
6、代码规范检测工具 SwiftLint
https://github.com/realm/SwiftLint
https://www.bbsmax.com/A/xl56GAykdr/
使用 cocoapods 引入:
pod 'SwiftLint'
Xcode 设置,在 Build Phases 中新增一个 Run Script:
"${PODS_ROOT}/SwiftLint/swiftlint"
配置自定义规则:
用命令行创建配置文件:
touch .swiftlint.yml
用命令行显示隐藏文件:
defaults write com.apple.finder AppleShowAllFiles -bool true
找到 .swiftlint.yml,开始设置规则:
规则参考:https://github.com/realm/SwiftLint
注意:使用的时候,将.swiftlint.yml 放在需要执行swiftlint工程的根目录中,整个工程会执行.swiftlint.yml的配置;
如果在二级目录同样配置了.swiftlint.yml文件,则会执行二级目录下的配置
7、常见第三方
网络类使用 Alamofire、moya
布局 snapkit
json处理引入 SwiftJson、HandyJSON
图片库 Kingfisher
响应式编程 RxSwift
加解密 CryptoSwift
数据库 SQLite.swift、WCDB.swift