1、Swift 調 OC
在橋接文件 SwiftDemo-Bridging-Header.h 里導入,如:
#import <MJRefresh/MJRefresh.h>
#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)..<welcome.endIndex
welcome.removeSubrange(range)
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