Swift语法手记

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

你可能感兴趣的:(Swift语法手记)