[iOS] 單例模式的深究

文章目錄

  • 前言
  • 一、什么是單例模式
  • 二、單例模式的優缺點
    • 優點
    • 缺點
  • 三、模式介紹
    • 1.懶漢模式(GCD & 互斥鎖)
      • GCD 寫法
      • 互斥鎖寫法(雙重檢查鎖)
    • 2.餓漢模式
  • 總結
      • 懶漢式 + 互斥鎖(Mutex)
      • **懶漢式 + GCD (dispatch_once) **
      • 餓漢式

前言

這篇博客是對單例模式的一個深入了解,之前的對單例模式的學習太過淺薄了,所以撰寫這篇博客來去深入對單例的理解。

一、什么是單例模式

單例模式 是一種常見的設計模式,核心思想是: 保證一個類在整個程序運行期間,只有唯一一個實例,并且提供一個全局訪問點。它可以做到大大減少內存的使用,防止一個實例被重復創建從而占用內存空間,他在共享資源和對象的情況下非常有用。

下面給出一些 OC 中常見的單例

  • NSUserDefaults → 用戶偏好設置
  • UIApplication → App 的入口對象
  • NSFileManager → 文件管理器
  • NSNotificationCenter → 通知中心

二、單例模式的優缺點

優點

  • 使整個應用內只有一個實例,避免數據沖突。
  • 提供了一個全局訪問的點,使用方便,不需要每次都 alloc/init。
  • 減少了內存開銷,節省資源。
  • 數據的一致性。

缺點

  • 因為是全局的狀態,增加了耦合,相當于一個全局變量,如果被濫用,會導致模塊之間的依賴性增強,代碼的維護度降低。
  • 單例對象的生命周期和應用的周期一樣長,很難被替換。
  • 在測試的時候很難被替換掉。
  • 使用單例的類,不顯式傳入依賴,而是全局取對象,導致代碼邏輯不透明。
  • 而且他因為生命周期太長導致一直持有大量資源,內存占用過高。

三、模式介紹

1.懶漢模式(GCD & 互斥鎖)

在這里我給出兩個懶漢模式

要知道在寫任何單例模式的時候都需要重寫三個方法因為我們要讓alloccopy還有mutablecopy這三個方法返回的單例保持一致,所以我們要重寫如下幾個方法。

+ (instancetype)allocWithZone:(struct _NSZone *)zone
- (id)copyWithZone:(NSZone *)zone
-(id)mutableCopyWithZone:(NSZone *)zone

GCD 寫法

這個寫法是蘋果公司最推薦的寫法,下面我會給出代碼以及解釋為什么要推薦這種寫法。

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface LazyGcdSingleton : NSObject<NSCopying, NSMutableCopying>
+ (instancetype) sharedInstance;
@endNS_ASSUME_NONNULL_END
//懶漢式GCD寫法
#import "LazyGcdSingleton.h"
static id _instance = nil;
@implementation LazyGcdSingleton
+ (instancetype)sharedInstance {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{_instance = [[super allocWithZone:NULL] init];});return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {return [LazyGcdSingleton sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {return [LazyGcdSingleton sharedInstance];
}
- (id) mutableCopyWithZone:(NSZone *)zone {return [LazyGcdSingleton sharedInstance];
}
@end
  1. static dispatch_once_t onceToken;
  • 定義了一個靜態變量 onceToken,它的作用就是 保證下面的 block 只會執行一次
  • dispatch_once_t 是蘋果專門設計的類型,用來做單例的線程安全初始化。
  1. dispatch_once(&onceToken, ^{ … });
  • dispatch_once 內部幫你做了 加鎖 + 判斷是否執行過 的工作。
  • 無論多少線程同時調用 sharedInstance,block 里的代碼只會被執行一次。
  • 所以這里的 _instance = [[super allocWithZone:NULL] init]; 只會執行一次,保證單例唯一性。
  1. _instance = [[super allocWithZone:NULL] init];
  • 真正創建對象的地方。
  • 用 super allocWithZone:NULL 是為了繞過 allocWithZone: 的重寫(因為我們在類里也重寫了它,防止外部直接用 alloc 創建新對象)。
  • 這樣寫就能保證只創建 一個實例對象

推薦這個寫法的最根本的原因就是 dispatch_once 他會幫助你加鎖和判斷是否執行過的工作。

互斥鎖寫法(雙重檢查鎖)

這里我給出代碼以及會給出一些讀者可能的疑問的解答

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface LazyLockSingleton : NSObject<NSCopying, NSMutableCopying>
+ (instancetype) sharedInstance;
@property (nonatomic, strong) NSString *info;@endNS_ASSUME_NONNULL_END
#import "LazyLockSingleton.h"
static id _instance = nil;
@implementation LazyLockSingleton
+ (instancetype)sharedInstance {if (_instance == nil) {@synchronized (self) {if (_instance == nil) {_instance = [[super alloc] init];}}}return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {if (_instance == nil) {@synchronized (self) {if (_instance == nil) {_instance = [super allocWithZone:zone];}}}return _instance;
}
- (id) copyWithZone:(NSZone *)zone {return [LazyLockSingleton sharedInstance];
}
- (id) mutableCopyWithZone:(NSZone *)zone {return [LazyLockSingleton sharedInstance];
}
@end

這里可能會有這樣幾個問題會有疑惑

1.是否可以這樣創建單例

+ (instancetype)sharedInstance {if (_instance == nil) {@synchronized (self) {_instance = [[super alloc] init];}}return _instance;
}

實則不行,我們只是鎖住了對象的創建,如果兩個線程同時進入 if,那么就會產生兩個對象。

2.為什么要用 static

如果不用 static,其他的類中可以使用 extern 來拿到這個單例

extern id instance;
instance = nil;

如果其他類中對單例進行如下操作,那么單例就會被重新創建,我們原本的單例對象中的內容就被銷毀了。

2.餓漢模式

餓漢模式是一種單例模式實現方式,它在類加載的時候就創建好唯一的實例,保證整個程序運行期間全局只有一個對象。

它有幾個非常奇妙的特點

  1. 線程安全
  • 因為實例在類加載階段就已經創建完成,不存在多線程同時創建的問題。
  1. 立即初始化
  • 無論程序是否會用到這個對象,它都會在類加載時創建。
  1. 簡單易實現
  • 只需一個靜態變量初始化即可,不需要加鎖或 dispatch_once。

缺點

  • 如果實例比較大或創建開銷高,而程序又不一定會用到,可能浪費內存和啟動時間。

下面給出代碼

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface EagerSingleton : NSObject<NSCopying, NSMutableCopying>
+ (instancetype) sharedInstance;
@property (nonatomic, strong) NSString *info;
@endNS_ASSUME_NONNULL_END
//餓漢式寫法
#import "EagerSingleton.h"
static id _instance;
@implementation EagerSingleton
+ (void)load {_instance = [[self alloc] init];
}
+ (instancetype)sharedInstance {return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {if (!_instance) {_instance = [super allocWithZone:zone];}return _instance;
}
- (id) copyWithZone:(NSZone *)zone {return _instance;
}
- (id) mutableCopyWithZone:(NSZone *)zone {return _instance;
}
@end

在這段代碼中我個人認為

+ (void)load {_instance = [[self alloc] init];
}

這段代碼完全體現了餓漢和懶漢的區別,它直接寫了一個類方法來去創建了一個后面需要用到的對象。

總結

其實有一個很形象的說法來去闡述餓漢和懶漢的區別就是餓漢就是直接吃飽再去干活,而懶漢就是等到需要吃的時候再去吃和干活,

下面我來總結一下懶漢(GCD & 互斥鎖)和餓漢

懶漢式 + 互斥鎖(Mutex)

  • 核心邏輯:使用 @synchronized(self) 對實例創建加鎖

  • 創建時機:第一次調用 sharedInstance 時才創建

  • 線程安全: 保證線程安全

  • 優點

    1. 按需創建(懶加載)
    2. 保證全局唯一
  • 缺點

    每次訪問都加鎖,性能較低

  • 使用場景:小型項目,理解單例原理

**懶漢式 + GCD (dispatch_once) **

  • 核心邏輯:使用 dispatch_once 保證 block 只執行一次
  • 創建時機:第一次調用 sharedInstance 時創建
  • 線程安全: 系統保證線程安全
  • 優點
    1. 按需創建(懶加載)
    2. 高效,只有第一次創建加鎖
    3. 代碼簡潔、官方推薦
  • 缺點:幾乎無明顯缺點
  • 使用場景:iOS / macOS 官方標準單例實現

餓漢式

  • 核心邏輯:在類加載階段就創建靜態實例

  • 創建時機:類加載時(程序啟動)

  • 線程安全: 天然線程安全

  • 優點

    1. 實現簡單
    2. 天然線程安全
  • 缺點

    程序啟動時就創建對象,如果對象大或未使用,可能浪費資源

  • 使用場景:必須全局存在的管理類,如配置管理器、日志管理器

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/96539.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/96539.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/96539.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

解決Discord.py中的/help命令問題

在使用Discord.py開發機器人時,常常會遇到一些常見的問題,比如命令找不到或者命令功能不符合預期。本文將詳細探討如何解決在使用@bot.slash_command定義/help命令時遇到的問題,并提供一個完整的實例來展示如何正確設置這個命令。 問題描述 當你在Discord機器人中輸入/hel…

解決VSCode默認F5配置無法啟動調試器的問題

前幾天做筆試&#xff0c;最后一題代碼有點問題&#xff0c;習慣性地按了個F5啟動gdb發現居然爆炸了&#xff0c;報錯找不到編譯出來的二進制文件&#xff0c;看著像是默認配置的問題&#xff0c;由于時間緊迫最后只能用輸出大法解決。 感覺不可理喻&#xff0c;幾年前調程序的…

webrtc弱網-LossBasedBweV2類源碼分析與算法原理

1. 核心功能LossBasedBweV2是WebRTC Google Congestion Control (GoogCC) 算法套件中的第二代基于丟包的帶寬估計器。它的核心功能是&#xff1a;帶寬估計&#xff1a; 根據網絡數據包的丟失情況&#xff0c;估算當前網絡路徑可用的帶寬上限。其核心假設是&#xff1a;當發送速…

AI代理化檢索:智能信息獲取新范式

代理化檢索(Agentic Retrieval)是一種由AI代理自主管理的信息檢索范式,通過動態規劃、工具調用和多步推理提升復雜查詢的處理能力。其核心機制、技術實現和應用特點如下: 一、核心機制 自主決策循環 代理通過循環執行"規劃-行動-觀察"流程處理查詢: 規劃階段:…

Android Studio中的各種Java版本區別

Android Studio中的各種Java版本 創建一個項目&#xff0c;app模塊的build.gradle.kts默認配置如下&#xff1a; plugins {alias(libs.plugins.android.application)alias(libs.plugins.kotlin.android) }android {namespace "cn.android666.javaversiontest"comp…

ubuntu新增磁盤擴展LV卷

登錄平臺 login as: wqbboy wqbboy172.17.2.86s password: Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-153-generic x86_64)* Documentation: https://help.ubuntu.com* Management: https://landscape.canonical.com* Support: https://ubuntu.com/proSyst…

Day 16: GAN生成對抗網絡專項 - 從博弈論到藝術創作的完整之旅

Day 16: GAN生成對抗網絡專項 - 從博弈論到藝術創作的完整之旅 ?? 學習目標: 深度掌握生成對抗網絡理論與實踐,從博弈論基礎到風格遷移應用的完整技術棧 ? 學習時長: 6小時深度學習 (理論3小時 + 實踐3小時) ?? 技術棧: PyTorch + 數學推導 + 經典架構 + 實戰應用 ?? 核…

《QT 108好類》之16 QComboBox類

《QT 108好類》之16 QComboBox類QT 108好類之16 QComboBox類QComboBox類特性和應用場景QComboBox類繼承關系QComboBox類使用1 簡單使用2 表單輸入3 使用自定義模型和視圖4 完全自定義彈出窗口QComboBox類類使用效果QT 108好類之16 QComboBox類 QComboBox是 常用的下拉框&#…

項目模塊劃分

項目模塊劃分 服務端模塊&#xff1a; 持久化數據管理中心模塊 在數據管理模塊中管理交換機&#xff0c;隊列&#xff0c;隊列綁定&#xff0c;消息等部分數據數據。 \1. 交換機管理&#xff1a; a. 管理信息&#xff1a;名稱&#xff0c;類型&#xff0c;是否持久化標志&#…

小白也能看懂!OpenCV 從零開始安裝配置全教程(包含Windows / Ubuntu / 樹莓派)系統詳細操作配置教程

小白也能看懂&#xff01;OpenCV 從零開始安裝配置全教程&#xff08;包含Windows / Ubuntu / 樹莓派&#xff09;系統詳細操作配置教程 摘要 本教程是面向“小白也能懂”的OpenCV安裝與配置全攻略&#xff0c;涵蓋Windows、Ubuntu和樹莓派三大平臺&#xff0c;真正實現“從零…

【華為云】容器鏡像服務 SWR 詳解:從上傳下載到 ModelArts 應用

前言 華為云容器鏡像服務&#xff08;Software Repository for Container&#xff0c;簡稱 SWR&#xff09;是華為云提供的企業級容器鏡像倉庫服務。它支持 Docker 鏡像的存儲、管理和分發&#xff0c;為容器化應用提供安全可靠的鏡像托管服務。本文將詳細介紹 SWR 的核心功能…

計算機網絡知識點梳理(一)概述:組成、發展、性能、體系結構等

目錄 一、互聯網 &#xff08;1&#xff09;特點 &#xff08;2&#xff09;網絡的組成 &#xff08;3&#xff09;網絡、互連網、因特網 &#xff08;4&#xff09;互聯網發展的三個階段 &#xff08;5&#xff09;標準化 &#xff08;6&#xff09;組成 二、計算機網…

不同行業視角下的數據分析

聲明&#xff1a;以下部分內容含AI生成 基于行業維度來劃分數據分析崗位&#xff0c;可以幫助我們更好地理解不同行業對數據分析技能、業務知識和職業發展的獨特要求。 目錄 一、總體框架&#xff1a;為什么行業維度如此重要&#xff1f; 二、主要行業劃分及詳細講解 1. 互聯…

「CTF」青少年CTF·雛形系統

題目&#xff1a; 解題過程 嘗試隨便輸入點什么&#xff0c;沒有結果 使用dirsearch掃描網址目錄 可以看到有掃描到一個www.zip&#xff0c;zip文件大概率有需要的東西 網址后加上www.zip就能對該文件進行下載 文件解壓縮后如下 打開qsnctf.php&#xff0c;代碼內容如下 <…

Java實戰項目演示代碼及流的使用

project 準備牌->洗牌->發牌 import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.TreeSet;public class PokerGameplus {static HashMap<Integer,String> hs new HashMap<>();static ArrayList<Int…

使用 OpenLayers + 高德瓦片源實現旅游足跡地圖

作為一個熱愛旅行的開發者&#xff0c;我一直想要一個能夠記錄和展示自己旅游足跡的功能。市面上雖然有很多地圖應用&#xff0c;但大多功能復雜&#xff0c;而我只需要一個簡單直觀的方式來標記去過的地方和想去的地方。 于是我決定在自己的個人網站上實現一個旅游足跡地圖功…

Redis基礎(含常用命令等以快速入門)

一、初步認識 1、NoSQL SQL 關系型數據庫&#xff08;表結構&#xff0c;強一致&#xff09;NoSQL 非關系型數據庫&#xff08;靈活結構&#xff0c;最終一致&#xff0c;水平擴展爽&#xff09; 維度SQL&#xff08;關系型&#xff09;NoSQL&#xff08;非關系型&#xf…

OSPF特殊區域、路由匯總及其他特性

OSPF路由器需要同時維護域內路由、域間路由、外部路由信息數據庫。當網絡規模不斷擴大時&#xff0c;LSDB規模也不斷增長。如果某區域不需要為其他區域提供流量中轉服務&#xff0c;那么該區域內的路由器就沒有必要維護本區域外的鏈路狀態數據庫。OSPF通過劃分區域可以減少網絡…

在緩存Cacheable注解中Key值如何使用常量

1.在常量類中定義商品緩存空間和商品緩存KEY public interface CacheConstants {/*** Goods Cache Name*/String QNA_GOODS_CACHE "qna-goods";/*** Goods Cache key*/String QNA_GOODS_CACHE_KEY "qna_goods:";/*** Order Cache Name*/String QNA_ORDER…

sklearn聚類

在此將sklearn官網的一張關于聚類算法比較的圖片放過來。 下面的表格是根據sklearn官網翻譯而來。 方法名稱 參數 可擴展性 應用場景 幾何度量(距離) MiniBatchKMeans 簇的數量 非常適合處理大量樣本和中等數量的簇(使用MiniBatch時) 通用型,適用于簇大小均勻、幾何形狀平…