Swift语法手记

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容