一、OC 到 Swift 基礎(chǔ)差異
-
1.1、提示符:MARK、TODO、FIXME、 #warning("")
-
<1>、
// MARK:類似于OC中的#pragma mark// MARK: 測試 func test() -> () { }// MARK:` 類似于OC中的 `#pragma mark -
<2>、
// MARK: -類似于OC中的#pragma mark -// MARK: - 上面的方法 // MARK: 方法一 func test() -> () { } // MARK: - 下面的方法 // MARK: 方法一 func test3() -> () { }// MARK: -` 類似于OC中的 `#pragma mark - -
<3>、
// TODO:用于標(biāo)記未完成的任務(wù)func test2() -> () { // TODO: 未完成的任務(wù) }// TODO: 用于標(biāo)記未完成的任務(wù) -
<4>、
// FIXME:用于標(biāo)記待修復(fù)的問題func test3() -> () { // FIXME: 待修復(fù) }// FIXME: 用于標(biāo)記待修復(fù)的問題 -
<5>、
#warning("")警告信息`#warning("")` 警告信息
-
-
1.2、條件編譯
// 操作系統(tǒng):macOS\iOS\tvOS\watchOS\Linux\Android\Windows\FreeBSD #if os(macOS) || os(iOS) // CPU架構(gòu):i386\x86_64\arm\arm64 #elseif arch(x86_64) || arch(arm64) // swift版本 #elseif swift(<5) && swift(>=3) // 模擬器 #elseif targetEnvironment(simulator) // 可以導(dǎo)入某模塊 #elseif canImport(Foundation) #else #endif- 自定義條件編譯
-
系統(tǒng)默認(rèn)的是
DEBUG,但是我們可以改名字,根據(jù)自己的需要了// debug模式 #if DEBUG // release模式 #else #endif #if TEST print("test") #endif #if OTHER print("other") #endif
-
- 自定義條件編譯
-
1.3、DUBUG 和 Release 模式下的打印
-
OC 中
#ifdef DEBUG #define JKLog(...) NSLog(@"%s 第%d行: %@\n\n",__func__,__LINE__,[NSString stringWithFormat:__VA_ARGS__]) #else #define JKLog(...) #endif -
Swift 中
/// 自定義打印 /// - Parameter msg: 打印的內(nèi)容 /// - Parameter file: 文件路徑 /// - Parameter line: 打印內(nèi)容所在的函數(shù) /// - Parameter fn: 打印內(nèi)容的函數(shù)名 func JKLog<T>(_ msg: T, file: NSString = #file, line:Int = #line, fn: String = #function) { #if DEBUG let prefix = "------\n當(dāng)前文件是:\(file.lastPathComponent)\n第 \(line) 行\(zhòng)n函數(shù)名:\(fn)\n打印內(nèi)容:msg\n------" print(prefix) #endif }
-
-
1.4、系統(tǒng)版本檢測
- 對于iOS平臺,只在iOS10及以上版本執(zhí)行
- 對于macOS平臺,只在macOS 10.12及以上版本執(zhí)行
- 最后的*表示在其他所有平臺都執(zhí)行
if #available(iOS 10, macOS 10.12, *) { } -
1.5、API 可用性說明
@available(iOS 10, macOS 10.15, *) class Person {} struct Student { // 方法更名 @available(*, unavailable, renamed: "study") func study_() {} func study() {} // 在某個系統(tǒng)下廢棄了 @available(iOS, deprecated: 11) @available(macOS, deprecated: 10.12) func run() {} }- 方法更名:
@available(*, unavailable, renamed: "study")
study_() 方法改為 study() 方法 - 更多的說明方法可以 參考官方
- 方法更名:
-
1.6、拓展小技巧
-
當(dāng)我們在寫一個函數(shù)的時候,可能里面不知道些什么,又不想讓它報錯,我們可以在作用域內(nèi)寫上:
fatalError()func testDo() -> Int { fatalError() }
-
二、Swift 項目說明
-
2.1、iOS 程序的入口
在AppDelegate上面默認(rèn)有個
@UIApplicationMain標(biāo)記,這表示:編譯器自動生成入口代碼(main函數(shù)代碼),自動設(shè)置AppDelegate為APP的代理-
也可以刪掉
@UIApplicationMain,自定義入口代碼:新建一個main.swift文件,如下:JKUIApplication使我們自定義的入口import UIKit class JKUIApplication: UIApplication {} UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, NSStringFromClass(JKUIApplication.self), NSStringFromClass(UIApplication.self))
-
2.2、Swift 調(diào)用 OC
- 先建一個橋接文件:直接見一個OC類,Xcode會直接提示我們創(chuàng)建一個橋接文件,文件名格式默認(rèn)為:
{targetName}-Bridging-Header.h
橋接文件
文件名格式默認(rèn)為: `{targetName}-Bridging-Header.h`
bridging 路徑 - 先建一個橋接文件:直接見一個OC類,Xcode會直接提示我們創(chuàng)建一個橋接文件,文件名格式默認(rèn)為:
-
在
{targetName}-Bridging-Header.h文件中#import OC需要暴露給Swift的內(nèi)容,如下-
自定義類 Animal, 其中 .h 和 .m文件內(nèi)容如下
// .h文件 #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN int sum(int a, int b); @interface Animal : NSObject @property (nonatomic, assign) NSInteger age; @property (nonatomic, copy) NSString *name; - (instancetype)initWithAge:(NSInteger)age name:(NSString *)name; + (instancetype)personWithAge:(NSInteger)age name:(NSString *)name; - (void)run; + (void)run; - (void)eat:(NSString *)food other:(NSString *)other; + (void)eat:(NSString *)food other:(NSString *)other; @end NS_ASSUME_NONNULL_END // .m 文件 #import "Animal.h" @implementation Animal - (instancetype)initWithAge:(NSInteger)age name:(NSString *)name { if (self = [super init]) { self.age = age; self.name = name; } return self; } + (instancetype)personWithAge:(NSInteger)age name:(NSString *)name { return [[self alloc] initWithAge:age name:name]; } + (void)run { NSLog(@"Animal +run"); } - (void)run { NSLog(@"%zd %@ -run", _age, _name); } + (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Animal +eat %@ %@", food, other); } - (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"%zd %@ -eat %@ %@", _age, _name, food, other); } @end int sum(int a, int b) { return a + b; } -
在 {targetName}-Bridging-Header.h 橋接文件里面 導(dǎo)入 OC的文件,如下
#import "Animal.h" -
調(diào)用 Swift 調(diào)用 OC 代碼
let p = Animal(age: 10, name: "Jack") p.age = 18 p.name = "Rose" // 18 Rose -run p.run() // 18 Rose -eat Apple Water p.eat("Apple", other: "Water") // Animal +run Animal.run() // Animal +eat Pizza Banana Animal.eat("Pizza", other: "Banana") // 30 print(sum(10, 20)) -
Swift 調(diào)用
@_silgen_name
如果我們在C 語言有一個方法和 swift 里面的方法重名,那么在調(diào)用的時候,在Swift項目里面會 優(yōu)先調(diào)用 swift 里面的方法,如果我們也想調(diào)在Swift里面調(diào)用C的方法,我們可以使用@_silgen_name修改 C 函數(shù)名// C語言 int sum(int a, int b) { return a + b; } // Swift @_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32 print(swift_sum(10, 20)) // 30 print(sum(10, 20)) // 30
-
-
2.3、OC 調(diào)用 Swift
Xcode已經(jīng)默認(rèn)生成一個用于OC調(diào)用Swift的頭文件,文件名格式是:{targetName}-Swift.h(1)、在 OC 的文件里面導(dǎo)入
{targetName}-Swift.h文件,(2)、要求 Swift要在OC里面使用的類繼承于
NSObject,如果 Swift 類里面的某個成員或者方法我們想要暴露給外面,就要在 某個成員或者方法 前面加@objc-
(3)、使用
@objcMembers修飾類
代表默認(rèn)所有成員都會暴露給OC(包括擴(kuò)展中定義的成員)
最終是否成功暴露,還需要考慮成員自身的訪問級別@objcMembers class Car: NSObject { var price: Double var band: String init(price: Double, band: String) { self.price = price self.band = band } func run() { print(price, band, "run") } static func run() { print("Car run") } } extension Car { func test() { print(price, band, "test") } }提示:Xcode會根據(jù)Swift代碼生成對應(yīng)的OC聲明,寫入{targetName}-Swift.h 文件
-
上述代碼在編譯后就會在
{targetName}-Swift.h文件 中生成如下代碼@interface Car : NSObject @property (nonatomic) double price; @property (nonatomic, copy) NSString * _Nonnull band; - (nonnull instancetype)initWithPrice:(double)price band:(NSString * _Nonnull)band OBJC_DESIGNATED_INITIALIZER; - (void)run; + (void)run; - (nonnull instancetype)init SWIFT_UNAVAILABLE; + (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); @end @interface Car (SWIFT_EXTENSION(JKSwiftDemo)) - (void)test; @end
在 OC 文件中使用Swift的類,先導(dǎo)入
#import "targetName-Swift.h"#import "targetName-Swift.h" Car *c = [[Car alloc] initWithPrice:10.5 band:@"BMW"]; c.band = @"Bently"; c.price = 108.5; [c run]; // 108.5 Bently run [c test]; // 108.5 Bently test [Car run]; // Car run-
通過
@objc重命名Swift暴露給OC的符號名(類名、屬性名、函數(shù)名等),如下@objc(JKCar) @objcMembers class Car: NSObject { var price: Double @objc(name) var band: String init(price: Double, band: String) { self.price = price self.band = band } @objc(drive) func run() { print(price, band, "run") } static func run() { print("Car run") } } extension Car { @objc(newTest) func test() { print(price, band, "test") } }在OC里面的調(diào)用
JKCar *c = [[JKCar alloc] initWithPrice:10.5 band:@"BMW"]; c.name = @"Bently"; c.price = 108.5; [c drive]; // 108.5 Bently run [JKCar run]; // Car run
-
-
2.4、根據(jù)2.2和2.3 列舉的幾個問題
- 1.為什么 Swift 暴露給 OC 的類最終要繼承自 NSObject?
答:因為 在OC 里面用類 ,必然繼承于 NSObject,在OC里面調(diào)用方法必然還要用到 runtime 那套流程,里面牽扯到 isa指針,isa指針來自NSObject - 2.p.run()底層是怎么調(diào)用的?反過來,OC調(diào)用 Swift 底層又是如何調(diào)用?
答:前面的:OC的東西在Swift里面調(diào)用,我們可以看到調(diào)用了runtime那套機(jī)制;后面的:Swift的東西在OC里面調(diào)用,打斷點(diǎn)看匯編可以發(fā)現(xiàn)調(diào)用的也是runtime那套機(jī)制 - 3.Swift 里面的 car.run() 底層是怎么調(diào)用的?
答:走的是Swift那套流程,如果我們強(qiáng)行讓它走OC那套runtime機(jī)制,可以在 run() 函數(shù)前加dynamic
- 1.為什么 Swift 暴露給 OC 的類最終要繼承自 NSObject?
-
2.5、選擇器(Selector)
Swift中依然可以使用選擇器,使用#selector(name)定義一個選擇器,但是 必須是被@objcMembers或@objc修飾的方法才可以定義選擇器@objcMembers class Person: NSObject { func test1(v1: Int) { print("test1") } func test2(v1: Int, v2: Int) { print("test2(v1:v2:)") } func test2(_ v1: Double, _ v2: Double) { print("test2(_:_:)") } func run() { perform(#selector(test1)) perform(#selector(test1(v1:))) perform(#selector(test2(v1:v2:))) perform(#selector(test2(_:_:))) perform(#selector(test2 as (Double, Double) -> Void)) } }
三、字符串
-
3.1、Swift的字符串類型String,跟OC的NSString,在API設(shè)計上還是有較大差異
-
空字符串
var emptyStr1 = "" var emptyStr2 = String() -
字符串前綴和后綴的判斷
var str = "123456" print(str.hasPrefix("123")) // true print(str.hasSuffix("456")) // true -
其他用法
var str: String = "1" // 拼接,jack_rose str.append("_2") // 重載運(yùn)算符 + str = str + "_3" // 重載運(yùn)算符 += str += "_4" // \()插值 str = "\(str)_5" // 長度,9,1_2_3_4_5 print(str.count)
-
-
3.2、String的插入和刪除
var str = "1_2" // 插入 單個字符,結(jié)果是:1_2_ str.insert("_", at: str.endIndex) // 插入 字符串,結(jié)果是:1_2_3_4 str.insert(contentsOf: "3_4", at: str.endIndex) // 在某個索引后面插入,結(jié)果是:1666_2_3_4 str.insert(contentsOf: "666", at: str.index(after: str.startIndex)) // 在某個索引后面插入,結(jié)果是:1666_2_3_8884 str.insert(contentsOf: "888", at: str.index(before: str.endIndex)) // 在某個索引后面插入,偏移索引,結(jié)果是:1666hello_2_3_8884 str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4)) // 刪除值為1的第一個索引的值,,結(jié)果是:666hello_2_3_8884 str.remove(at: str.firstIndex(of: "1")!) // 刪除值為字符為 6 的字符,結(jié)果是:hello_2_3_8884 str.removeAll { $0 == "6" } //刪除某個區(qū)間的字符 var range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex) // hello_2_3_4 str.removeSubrange(range) -
3.3、Substring 子串
String可以通過下標(biāo)、 prefix、 suffix等截取子串,子串類型不是String,而是Substring
Substring和它的base,共享字符串?dāng)?shù)據(jù)
-
Substring發(fā)生修改 或者 轉(zhuǎn)為String時,會分配新的內(nèi)存存儲字符串?dāng)?shù)據(jù),也就是深度拷貝
var str = "1_2_3_4_5" // 1_2 var substr1 = str.prefix(3) // 4_5 var substr2 = str.suffix(3) // 1_2 var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3) var substr3 = str[range] // 最初的String,1_2_3_4_5 print(substr3.base) // Substring -> String var str2 = String(substr3)-
prefix(3)代表從 頭 截取三位 -
suffix(3)代表從 尾 截取三位 - 子串在沒有進(jìn)行修改前 和 原字符串公用一塊內(nèi)存,在子串進(jìn)行修改后,那么就要進(jìn)行深度拷貝了
-
-
3.4、String 與 Character
for c in "jack" { // c是Character類型 print(c) } var str = "jack" // c是Character類型 var c = str[str.startIndex] -
3.5、String 相關(guān)的協(xié)議
- BidirectionalCollection 協(xié)議包含的部分內(nèi)容
- startIndex 、 endIndex 屬性、index 方法
- String、Array 都遵守了這個協(xié)議
- RangeReplaceableCollection 協(xié)議包含的部分內(nèi)容
- append、insert、remove 方法
- String、Array 都遵守了這個協(xié)議
- Dictionary、Set 也有實現(xiàn)上述協(xié)議中聲明的一些方法,只是并沒有遵守上述協(xié)議
- BidirectionalCollection 協(xié)議包含的部分內(nèi)容
-
3.6、多行String
-
放在 三個雙引號之間的代表是多行,如下
let str = """ 1 "2" 3 '4' """ -
如果需要顯示三個 引號,至少轉(zhuǎn)義一個引號
let str = """ Escaping the first quote \""" Escaping two quotes \"\"" Escaping all three quotes \"\"\" """ -
縮進(jìn)以結(jié)尾的 三引號為對齊線
let str = """ 1 "2" 3 '4' """ -
以下兩個字符串是等價的
let str1 = "These are the same." let str2 = """ These are the same. """
-
-
3.7、String 與 NSString
String 與 NSString 之間可以隨時隨地橋接轉(zhuǎn)換
-
如果你覺得String的API過于復(fù)雜難用,可以考慮將String轉(zhuǎn)為NSString
var str1: String = "jack" var str2: NSString = "rose" var str3 = str1 as NSString var str4 = str2 as String // OC的使用 var str5 = str3.substring(with: NSRange(location: 0, length: 2)) print(str5) -
比較字符串內(nèi)容是否等價
String使用 == 運(yùn)算符 NSString使用 isEqual 方法,也可以使用 == 運(yùn)算符(本質(zhì)還是調(diào)用了isEqual方法) -
Swift、OC橋接轉(zhuǎn)換表
Swift、OC橋接轉(zhuǎn)換表- 提示:不可以由
不可變強(qiáng)轉(zhuǎn)成可變的
- 提示:不可以由
四、OC 與 Swift 其他的不同點(diǎn)
-
4.1、只能被class繼承的協(xié)議
protocol Runnable1: AnyObject {} protocol Runnable2: class {} @objc protocol Runnable3 {}被
@objc修飾的協(xié)議,還可以暴露給OC去遵守實現(xiàn) -
4.2.可選協(xié)議
-
第一種:可以通過
@objc定義可選協(xié)議,這種協(xié)議只能被class 遵守@objc protocol Runnable { func run1() @objc optional func run2() func run3() } class Dog: Runnable { func run3() { print("Dog run3") } func run1() { print("Dog run1") } } var d = Dog() d.run1() // Dog run1 d.run3() // Dog run3 -
第二種:我們可以通過擴(kuò)展,如下Dog類就不需要實現(xiàn)
run2(),因為擴(kuò)展中已經(jīng)實現(xiàn)protocol Runnable { func run1() func run2() } extension Runnable { func run2(){ } } class Dog: Runnable { func run1() { print("Dog run1") } } var d = Dog() d.run1() // Dog run1
-
-
4.3、dynamic
被@objc dynamic修飾的內(nèi)容會具有動態(tài)性,比如調(diào)用方法會走runtime那一套流程class Dog: NSObject { @objc dynamic func test1() {} func test2() {} } var d = Dog() d.test1() d.test2()
-
4.4、KVO / KVC
Swift 支持 KVC \ KVO 的條件 ,必須滿足以下條件
(1)、屬性所在的類、監(jiān)聽器最終繼承自 NSObject
-
(2)、 用
@objc dynamic修飾對應(yīng)的屬性class Observer: NSObject { override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { print("observeValue", change?[.newKey] as Any) } } class Person: NSObject { @objc dynamic var age: Int = 0 var observer: Observer = Observer() override init() { super.init() self.addObserver(observer, forKeyPath: "age", options: .new, context: nil) } deinit { self.removeObserver(observer, forKeyPath: "age") } } var p = Person() // observeValue Optional(20) p.age = 20 // observeValue Optional(25) p.setValue(25, forKey: "age")
-
4.5、Block式的KVO
class Person: NSObject { @objc dynamic var age: Int = 0 var observation: NSKeyValueObservation? override init() { super.init() observation = observe(\Person.age, options: .new) { (person, change) in print(change.newValue as Any) } } } var p = Person() // Optional(20) p.age = 20 // Optional(25) p.setValue(25, forKey: "age") -
4.6、關(guān)聯(lián)對象
在Swift中,class依然可以使用關(guān)聯(lián)對象
-
默認(rèn)情況,extension不可以增加存儲屬性 ,借助關(guān)聯(lián)對象,可以實現(xiàn)類似extension為class增加存儲屬性的效果
class Person {} extension Person { private static var AGE_KEY: Void? var age: Int { get { (objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0 } set { objc_setAssociatedObject(self, &Self.AGE_KEY, newValue, .OBJC_ASSOCIATION_ASSIGN) } } } var p = Person1() print(p.age) // 0 p.age = 10 print(p.age) // 10提示:唯一的存儲空間
private static var AGE_KEY: Void?,我們使用Void和Bool 都是 1 個存儲空間,節(jié)省內(nèi)存
-
4.7、資源名管理
-
平時的做法:直接加載圖片名字或者按鈕的名字,如下
let img = UIImage(named: "logo") let btn = UIButton(type: .custom) btn.setTitle("添加", for: .normal) -
優(yōu)化后的做法,先定義一個資源枚舉 JKResource
enum JKResource { /// 按鈕名字 enum string: String { case add = "添加" } /// 圖片名字 enum image: String { case logo } enum segue: String { case login_main } } // 調(diào)用 let img = UIImage(named: JKResource.image.logo.rawValue) let btn = UIButton(type: .custom) btn.setTitle(JKResource.string.add.rawValue, for: .normal)- 提示:這種做法實際上是參考了Android的資源名管理方式
-
通過擴(kuò)展進(jìn)一步管理資源名
extension UIImage { convenience init?(_ name: R.image) { self.init(named: name.rawValue) } } extension UIButton { func setTitle(_ title: R.string, for state: UIControl.State) { setTitle(title.rawValue, for: state) } } -
資源名管理的其他思路
enum JKResource { enum image { static var logo = UIImage(named: "logo") } enum font { static func arial(_ size: CGFloat) -> UIFont? { UIFont(name: "Arial", size: size) } } } // 使用如下 let img = JKResource.image.logo let font = JKResource.font.arial(14)更多優(yōu)秀的思路參考如下
-
五、多線程
-
5.1、多線程開發(fā)-異步
public typealias Task = () -> Void public struct JKAsyncs { public static func async(_ task: @escaping Task) { _async(task) } public static func async(_ task: @escaping Task, _ mainTask: @escaping Task) { _async(task, mainTask) } private static func _async(_ task: @escaping Task, _ mainTask: Task? = nil) { let item = DispatchWorkItem(block: task) DispatchQueue.global().async(execute: item) if let main = mainTask { item.notify(queue: DispatchQueue.main, execute: main) } } } // 調(diào)用如下 JKAsyncs.async({ print(Thread.current) // 自線程 }) { print(Thread.current) // 主線程 }提示:開辟線程任務(wù)可能是在大括號之外完成所以加上
@escaping:逃逸閉包-
DispatchWorkItem的使用,子線程和其他線程分開,更加的直觀let item = DispatchWorkItem { print(Thread.current) } DispatchQueue.global().async(execute: item) item.notify(queue: DispatchQueue.main) { print(Thread.current) }
-
-
5.2、多線程開發(fā)-主線程延遲
-
平時的用法
let seconds:Double = 5 let item = DispatchWorkItem { print("\(seconds)秒后打印",Thread.current) } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds, execute: item) -
寫到封裝的
JKAsyncs結(jié)構(gòu)體里面@discardableResult public static func delay(_ seconds: Double, _ block: @escaping Task) -> DispatchWorkItem { let item = DispatchWorkItem(block: block) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds, execute: item) return item }提示:
@discardableResult代表在使用的時候我們可以忽略函數(shù)的返回值,也就是可以不用接收返回值- 返回值itme的作用:
item.cancel()取消 方法的執(zhí)行,也就是取消延遲 - 延遲操作是在子線程
- 返回值itme的作用:
-
-
5.3、多線程開發(fā)-異步延遲
@discardableResult public static func asyncDelay(_ seconds: Double, _ task: @escaping Task) -> DispatchWorkItem { return _asyncDelay(seconds, task) } @discardableResult public static func asyncDelay(_ seconds: Double, _ task: @escaping Task, _ mainTask: @escaping Task) -> DispatchWorkItem { return _asyncDelay(seconds, task, mainTask) } private static func _asyncDelay(_ seconds: Double, _ task: @escaping Task, _ mainTask: Task? = nil) -> DispatchWorkItem { let item = DispatchWorkItem(block: task) DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds, execute: item) if let main = mainTask { item.notify(queue: DispatchQueue.main, execute: main) } return item } -
5.4、多線程開發(fā) - once:一次性代碼,
dispatch_once在 Swift 中已被廢棄,取而代之 如下方式-
可以用類型屬性或者全局變量\常量(整個程序啟動后只有一份內(nèi)存)
fileprivate let initTask2: Void = { print("initTask2---------") }() class ViewController: UIViewController { static let initTask1: Void = { print("initTask1---------") }() override func viewDidLoad() { super.viewDidLoad() let _ = Self.initTask1 let _ = initTask2 let _ = Self.initTask1 let _ = initTask2 } }打印結(jié)果
initTask1--------- initTask2---------提示:默認(rèn)自帶
lazy + dispatch_once效果- 第一個字母大寫的
Self代表當(dāng)前的類 - 懶加載的屬性里面只會走一次
- 第一個字母大寫的
-
-
5.5、多線程開發(fā)-加鎖(線程同步技術(shù),防止資源搶奪)
-
第一種鎖:gcd 信號量
class Cache { private static var data = [String: Any]() // 設(shè)置信號量的鎖 private static var lock = DispatchSemaphore(value: 1) static func get(_ key: String) -> Any? { data[key] } static func set(_ key: String, _ value: Any) { // 加鎖 lock.wait() defer { // 解鎖 lock.signal() } data[key] = value } } -
NSLock鎖
class Cache { private static var data = [String: Any]() private static var lock = NSLock() static func get(_ key: String) -> Any? { data[key] } static func set(_ key: String, _ value: Any) { // 加鎖 lock.lock() defer { // 解鎖 lock.unlock() } data[key] = value } 遞歸鎖:如果一個調(diào)用存在調(diào)用自身(遞歸),那么我們就是用遞歸鎖:
NSRecursiveLock(),加鎖解鎖和上面 NSLock鎖 一樣
-














