国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Swift > 正文

深入理解Swift中單例模式的替換及Swift 3.0單例模式的實現

2020-03-09 17:43:46
字體:
來源:轉載
供稿:網友

前言

除了 MVC、MVVM 之外,單例模式可以說是 iOS 開發中另一常見的設計模式。無論是 UIKit 或是一些流行的三方庫,我們都能看到單例的身影。而我們開發者本身也會潛意識地將這些類庫中的代碼當作最佳實踐并將其帶入日常工作中,哪怕很多人都知道單例存在一些明顯的缺陷。

針對單例的缺陷,本文將介紹一些替換或改造單例模式的方法來提升代碼質量。

單例的優點

除了上面提到的模仿最佳實踐之外,單例的流行肯定也有內在的原因和理由。例如:單例對象保證了只有一個實例的存在,這樣有利于我們協調系統整體的行為。比如在某個服務器程序中,該服務器的配置信息存放在一個文件中,這些配置數據由一個單例對象統一讀取,然后服務進程中的其他對象再通過這個單例對象獲取這些配置信息。這種方式簡化了在復雜環境下的配置管理。 另一方面,全局單一對象也減少了不必要的對象創建和銷毀動作提高了效率。

下面是一個典型的單例模式代碼:

class UserManager { static let shared = UserManager()  private init() { // 單例模式,防止出現多個實例 }  ....}extension UserManager { func logOut( ) { ... }  func logIn( ) { ... }}class ProfileViewController: UIViewController { private lazy var nameLabel = UILabel() override func viewDidLoad() { super.viewDidLoad() nameLabel.text = UserManager.shared.currentUser?.name } private func handleLogOutButtonTap() { UserManager.shared.logOut() }}

單例的缺陷

雖然上面提到了單例的一些優點,但是這不能掩蓋單例模式一些明顯的缺陷:

  • 全局共享可修改的狀態:單例模式的副作用之一就是那些共享狀態量在 app 的生命周期內都可能發生修改,而這些修改可能造成一些位置錯誤。更為糟糕的是因為作用域和生命周期的特性,這些問題還非常難定位。
  • 依賴關系不明確:因為單例在全局都非常容易進行訪問,這將是我們的代碼變成所謂的 意大利面條 式的代碼。單例與使用者的關系界限不明確,后期維護也非常麻煩。
  • 難以追蹤測試:因為單例模式與 app 擁有同樣的生命周期而生命周期內進行的任意修改,所以無法確保一個干凈的實例用于測試。
  • 由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難。
  • 單例類的職責過重,在一定程度上違背了 “單一職責原則”。

依賴注入

與之間之間使用單例對象不同,這里我們可以在初始化是進行依賴注入。

class ProfileViewController: UIViewController { private let user: User private let logOutService: LogOutService private lazy var nameLabel = UILabel() init(user: User, logOutService: LogOutService) { self.user = user self.logOutService = logOutService super.init(nibName: nil, bundle: nil) } override func viewDidLoad() { super.viewDidLoad() nameLabel.text = user.name } private func handleLogOutButtonTap() { logOutService.logOut() }}class LogOutService { private let user: User private let networkService: NetworkService private let navigationService: NavigationService init(user: User, networkService: NetworkService, navigationService: NavigationService) { self.user = user self.networkService = networkService self.navigationService = navigationService } func logOut() { networkService.request(.logout(user)) { [weak self] in self?.navigationService.showLoginScreen() } }}

上面代碼中的依賴關系明顯比之前更為清晰,而且也更方便后期維護和編寫測試實例。另外,通過 LogOutService 對象我們將某些特定服務抽離了出來,避免了單例中常見的臃腫狀態。

協議化改造

將一個單例濫用的應用一次性全面改寫為上面那樣的依賴注入和服務化顯然是一件非常耗時且不合理的事情。所以下面將會介紹通過協議對單例進行逐步改造的方法,這里主要的做法就是將上面 LogOutService 提供的服務改寫為協議:

protocol LogOutService { func logOut()}protocol NetworkService { func request(_ endpoint: Endpoint, completionHandler: @escaping () -> Void)}protocol NavigationService { func showLoginScreen() func showProfile(for user: User) ...}

定義好協議服務之后,我們讓原有的單例遵循該協議。此時我們可以在不修改原有代碼實現的同時將單例對象當作服務進行依賴注入。

extension UserManager: LoginService, LogOutService {}extension AppDelegate: NavigationService { func showLoginScreen() { navigationController.viewControllers = [ LoginViewController( loginService: UserManager.shared, navigationService: self ) ] } func showProfile(for user: User) { let viewController = ProfileViewController( user: user, logOutService: UserManager.shared ) navigationController.pushViewController(viewController, animated: true) }}

Swift3.0 單例模式實現的幾種方法-Dispatch_Once

在開發中需要使用單例模式是再尋常不過的了,正常我們的思路是使用GCD的dispatch_once這個API來寫,然而在swift/274756.html">swift3.0中,蘋果已經廢棄了這個方法,不過不用擔心,我們可以用別的方式來實現。

結合swift語言的特性,總結了以下幾種寫法:

  • 普通創建法
  • 靜態創建法
  • struct創建法
  • 通過給DIspatchQueue添加擴展實現

注意:這里我希望大家除了使用還要會調用該對應的方法

1.普通創建法

//MARK - : 單例:方法1 static let shareSingleOne = Single()

2.靜態創建法

let single = Single()class Single: NSObject { //-MARK: 單例:方法2 class var sharedInstance2 : Single {  return single }}

3.struct創建法

 //-MARK: 單例:方法3 static var shareInstance3:Single{ struct MyStatic{  static var instance :Single = Single() } return MyStatic.instance; }

4.通過給DispatchQueue添加擴展實現

public extension DispatchQueue {   private static var _onceTracker = [String]()   /**  Executes a block of code, associated with a unique token, only once. The code is thread safe and will  only execute the code once even in the presence of multithreaded calls.   - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID  - parameter block: Block to execute once  */  public class func once(token: String, block:()->Void) {  objc_sync_enter(self)  defer { objc_sync_exit(self) }   if _onceTracker.contains(token) {   return  }   _onceTracker.append(token)  block()  } } 

使用字符串token作為once的ID,執行once的時候加了一個鎖,避免多線程下的token判斷不準確的問題。

使用的時候可以傳token

DispatchQueue.once(token: "com.vectorform.test") {  print( "Do This Once!" ) } 

或者使用UUID也可以:

private let _onceToken = NSUUID().uuidString  DispatchQueue.once(token: _onceToken) {  print( "Do This Once!" ) } 

結語

單例模式并不是毫無可取之處,例如在日志服務、外設管理等場景下還是非常適用的。但是大多數時候單例模式由于依賴關系不明確以及全局共享可變狀態可能會增加系統的復雜度造成一系列未知問題。如果你當前的代碼中使用了大量的單例模式的話,我希望本文能夠幫你從中解脫出來構建一個更健壯的系統。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到swift教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 罗江县| 周口市| 合作市| 山东省| 平度市| 通辽市| 宁化县| 新民市| 蒙自县| 明溪县| 高唐县| 南华县| 高州市| 肇东市| 盐亭县| 沾益县| 舟曲县| 洛扎县| 陆川县| 海口市| 廉江市| 明溪县| 武城县| 凌海市| 谢通门县| 河源市| 高邑县| 石渠县| 股票| 旬邑县| 安新县| 汾西县| 定南县| 仪陇县| 汉阴县| 黄石市| 江阴市| 牡丹江市| 吉林省| 赫章县| 安阳县|