【iOS】 單例模式

1. 認識單例模式

請添加圖片描述

首先讓我們先看下關于單例模式的定義(來自于《設計模式》(Addison-Wesley,1994))

一個類有且僅有一個實例,并且自行實例化向整個系統提供。

如果說每一個人都是一個類,那么從他出生開始,他就是生活中的唯一實例,每當有人要拜訪或者聯系你的時候,無論別人認識你的時候你是什么狀態,他所能聯系到的都是現在的你。你本身的狀態會在其他地方發生改變,即當你狀態改變后,后續所有找到你的,都是看到狀態改變后的你。那么,我們就可以認為每一個人都處于單例模式。

在 iOS 中,系統默認就有許多支持單例方法的地方,例如通知中心、應用管理、本地保存等,但是一般都屬于非嚴格的單例模式,也就是你可以使用 [UIApplication sharedApplication] 來獲取單例對象,也可以通過 [UIApplication new] 生成一個新的對象,而如果按照準確的單例定義來說,依舊以 UIApplication 類為例, [UIApplication new][UIApplication sharedApplication] 應該在任何時候返回的對象都是相同的。也就是說無論你怎么操作,只會存在一個實例,不會創建其他的副本內容。

2. 單例模式的使用

單例模式需要實現一個公共訪問的類方法,一般命名為 shared + 類名。在該方法的具體實現方案,是推薦通過dispatch_once 來實現類的實例化。

可以直接通過重寫父類的方法,把分配內存的方法變成只執行一次。從根本上實現了單例。

如果按照嚴格的單例寫法的話,單例模式一共需要重寫五個方法 :

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

或者,可以把這些方法全都通過attribute((unavailable (invalid ))); 方式禁止,以保證一定使用shhared的單例方法。

SingleView.h#import <Foundation/Foundation.h>@interface SingleView : NSObject#pragma mark- method
+ (SingleView *)sharedSingleView;
+ (id)alloc __attribute__((unavailable("invalid, use sharedSingleView instead")));
+ (id)new __attribute__((unavailable("invalid, use sharedSingleView instead")));
- (id)copy __attribute__((unavailable("invalid, use sharedSingleView instead")));
- (id)mutableCopy __attribute__((unavailable("invalid, use sharedSingleView instead")));@end

截屏2022-07-31 12.03.41.png

如果只是在自己的項目中使用的話,那么直接實現一個 shared 的類方法基本都能滿足需求,由于是自己的內容,代碼是受控的,實現并調用即可。而如果是對外的庫的話(靜態庫、動態庫),這需要根據具體的業務內容考慮是否需要按照嚴格的單例寫法來實現了。

懶漢

懶漢式創建單例模式的意思其實就是指我們在創建的時間是我們需要用到這個單例的時候,我們才開始創建這個唯一實例,這種創建模式有助于提高性能,以及節省資源的效果。簡單來說,就是我們平時日常生活中的deadline,只要在達到deadline的時候我們才去提交工作,這和他的名字也很相似,懶漢式延遲創建。

dispatch_once

//SingleView.h#import <Foundation/Foundation.h>@interface SingleView : NSObject
+ (SingleView *)sharedSingleView;
@end
//SingleView.m#import "SingleView.h"@implementation SingleView#pragma mark- public method
+ (SingleView *)sharedSingleView {static SingleView *single = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{single = [[super allocWithZone:NULL] init];});return single;
}+ (instancetype)allocWithZone:(struct _NSZone *)zone {return [SingleView shareSingleView];
}+ (id)copyWithZone:(struct _NSZone *)zone {return [SingleView shareSingleView];
}+ (id)mutableCopyWithZone:(struct _NSZone *)zone {return [SingleView shareSingleView];
}- (id)copyWithZone:(NSZone *)zone{return  [SingleView shareSingleView];
}- (id)mutableCopyWithZone:(NSZone *)zone{return [SingleView shareSingleView];
}
@end

同步鎖實現

static Singleton* instance = nil;
+(id) sharedInstance  {if (instance == nil) {@synchronized (self) {if (instance == nil) {instance = [[super allocWithZone:NULL] init];}}}return instance;
}+(id) allocWithZone:(struct _NSZone *)zone {if (instance == nil) {@synchronized (self) {if (!instance) {instance = [[super allocWithZone:NULL] init];}}}return instance;
}-(id) copyWithZone:(NSZone *)zone {return self;
}-(id) mutableCopyWithZone:(NSZone *)zone {return  self;
}
@end

餓漢

餓漢式創建單例則是在你的類加載的時候立刻就開始加載一個單例的一種單例模式,這種模式我們需要把我們的加載單例的代碼寫在類第一次加載的位置。

static id instance = nil;+ (void)load {instance = [[super allocWithZone:NULL] init];
}+ (instancetype)sharedInstance {return instance;
}+ (instancetype)allocWithZone:(struct _NSZone *)zone {return instance;
}- (instancetype)init {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{NSLog(@"init一次");});return self;
}- (id)copyWithZone:(NSZone *)zone {return self;
}- (id)mutableCopyWithZone:(NSZone *)zone {return self;
}

輸出結果如下證明單例代碼有效:
請添加圖片描述

因為對block部分對不了解,引用學長博客中的一段話:

ispatch_once 主要是根據 onceToken 的值來決定怎么去執行代碼。
1.當 onceToken = 0 時,線程執行 dispatch_once 的 block 中代碼;
2.當 onceToken = -1 時,線程跳過 dispatch_once 的 block 中代碼不執行;
3.當 onceToken 為其他值時,線程被阻塞,等待 onceToken 值改變。
當線程調用mySingleton方法時,此時 onceToken = 0,調用 block 中的代碼,此時 onceToken =其他值。
當其他線程再調用 mySingleton 方法時,onceToken為其他值,線程阻塞。當 block 線程執行完 block之后,onceToken = -1,其他線程不再阻塞,跳過 block。下次再調用mySingleton方法 時, block 已經為-1,直接跳過 block。轉載自[【iOS】—— 單例模式]
(https://blog.csdn.net/m0_73974920/article/details/132909916?spm=1001.2014.3001.5502)
這里又要注意一下要調用父類的【super allocWithZone:null】這個方法,因為我們這里已經重寫這個類的方法了,不然會出現一個循環調用的問題。同時為了防止copy和mutablecopy兩個方法的出現崩潰的問題。

3.總結

單例模式是一種非常常見、使用頻率也很高的一種設計模式。單例能夠在許多場合使用,有時候也可以用來保存數據。
它(嚴格單例)存在著以下特點:

節省內存;
在程序的運行周期中保存數據;
只能在自己的類中實現實例;
程序的運行周期中,內存不會被釋放;
類中有且僅有一個實例;

懶漢模式:

· 優點:
延遲加載:懶漢模式只有在第一次訪問單例實例時才會進行初始化,可以節省資源,提高性能,因為實例只有在需要時才會被創建。
節省內存:如果單例對象很大或者初始化過程開銷較大,懶漢模式可以避免在程序啟動時就創建不必要的對象。
線程安全性:可以通過加鎖機制(如雙重檢查鎖定)來實現線程安全。

· 缺點:
線程安全性開銷:懶漢模式在實現線程安全時可能需要額外的同步機制,這會引入一些性能開銷。
復雜性增加:實現線程安全的懶漢模式可能需要編寫復雜的代碼,容易引入錯誤。
缺點:
線程安全性開銷:懶漢模式在實現線程安全時可能需要額外的同步機制,這會引入一些性能開銷。
復雜性增加:實現線程安全的懶漢模式可能需要編寫復雜的代碼,容易引入錯誤。

餓漢模式:

優點:
簡單:餓漢模式實現簡單,不需要考慮線程安全問題,因為實例在類加載時就已經創建。
線程安全性:由于實例在類加載時創建,不會存在多個實例的風險,因此線程安全。

缺點:
無法實現延遲加載:餓漢模式在程序啟動時就創建實例,無法實現延遲加載,可能會浪費資源,特別是當實例很大或初始化開銷較大時。
可能引起性能問題:如果單例類的實例在程序啟動時沒有被使用,那么創建實例的開銷可能是不必要的。
不適用于某些情況:如果單例對象的創建依賴于某些外部因素,而這些因素在程序啟動時無法確定,那么餓漢模式可能不適用。

總的來說,懶漢模式適用于需要延遲加載實例的情況,可以節省資源和提高性能,但需要考慮線程安全性。餓漢模式適用于需要簡單實現和線程安全性的情況,但不支持延遲加載。選擇哪種模式應根據具體需求和性能考慮來決定。

請添加圖片描述

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

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

相關文章

多目標輪廓匹配

前面我們使用模板匹配&#xff0c;得到的結果都是一個圖&#xff0c;那么如果我們圖片中有許多我們的目標&#xff0c;那么該如何找出來呢&#xff1f;如上我們圖片中有許多箭頭和我們的模板一致&#xff0c;只不過方向不對&#xff0c;那么該如何匹配呢&#xff1f;圖片和模板…

【C++】簡單介紹lambda表達式

各位大佬好&#xff0c;我是落羽&#xff01;一個堅持不斷學習進步的學生。 如果您覺得我的文章還不錯&#xff0c;歡迎多多互三分享交流&#xff0c;一起學習進步&#xff01; 也歡迎關注我的blog主頁: 落羽的落羽 文章目錄一、 什么是lambda表達式二、 表達式語法三、lambd…

磁共振成像原理(理論)4:自由進動和弛豫 (Free Precession and Relaxation)

當磁化自旋系統被射頻脈沖擾動而偏離其熱平衡態后&#xff0c;一旦移除外部激勵并給予足夠時間&#xff0c;系統將根據熱力學定律返回平衡態。這一過程包含三個特征現象&#xff1a; (a) 自由進動——宏觀磁化矢量 (M?\vec{M}M) 繞( B0?\vec {B_0}B0?? )場的進動&#xff1…

ubuntu 20.04 安裝spark

安裝openjdk21 下載 wget https://download.java.net/openjdk/jdk21/ri/openjdk-2135_linux-x64_bin.tar.gz解壓 tar -xvf openjdk-2135_linux-x64_bin.tar.gzsudo mv jdk-21/ /opt/jdk-21/設置環境變量 echo export JAVA_HOME/opt/jdk-21 | sudo tee /etc/profile.d/java2…

第三方區塊鏈應用測評:【多簽錢包合約安全評估_閾值簽名機制與私鑰存儲安全性測試】

閾值簽名機制安全測試密碼學審計 采用門限簽名方案&#xff08;TSS&#xff09;的多簽錢包需驗證其閾值BLS簽名或ECDSA簽名算法的正確性。測試重點包括&#xff1a;分布式密鑰生成&#xff08;DKG&#xff09;過程的保密性&#xff08;無密鑰信息泄露&#xff09;、簽名碎片驗證…

大模型處理長文檔的挑戰和解決方案?

當前&#xff0c;AI 應用正處于極速發展階段&#xff0c;大語言模型&#xff08;LLM&#xff09;與檢索增強生成&#xff08;RAG&#xff09;系統已成為構建智能問答、知識管理等高階 AI 應用的核心引擎&#xff0c;被廣泛應用于金融分析、學術研究、企業合規等多個領域。然而&…

JavaWeb--day1--HTMLCSS

(以下內容全部來自上述課程及課件) web開發介紹 1. 什么是web&#xff1f; Web&#xff1a;全球廣域網&#xff0c;也稱為萬維網&#xff0c;能夠通過瀏覽器訪問的網站。 2. Web網站的工作流程 3. Web標準 Web標準也稱為網頁標準&#xff0c;由一系列的標準組成&#xf…

OpenEuler安裝gitlab,部署gitlab-runner

目錄 一、安裝gitlab 二、安裝部署docker設置源 三、下載部署runner ?編輯 四、構建CI/CD 一、安裝gitlab 1.查看OpenEuler版本 [rootlocalhost ~]# cat /etc/os-release NAME"openEuler" VERSION"24.03 (LTS-SP1)" ID"openEuler" VERSI…

實戰項目-----在圖片 hua.png 中,用紅色畫出花的外部輪廓,用綠色畫出其簡化輪廓(ε=周長×0.005),并在同一窗口顯示

實戰項目實現以下功能&#xff1a;對圖片 hua.png 進行輪廓提取&#xff0c;并在同一窗口中完成以下兩個繪制操作&#xff1a;用紅色畫出花的外部輪廓&#xff08;即最外層輪廓&#xff09;用綠色畫出該輪廓的近似多邊形&#xff0c;其中近似精度參數 ε 設置為輪廓周長的 0.00…

開源鴻蒙北向框架開發:系統服務理論詳解

系統服務的啟動 基本可以認為&#xff1a;OpenHarmony 的系統服務進程都是“由 init 直接或間接拉起”的。 直接方式&#xff1a; init 按 /system/etc/init/.cfg 啟動可執行&#xff08;如 /system/bin/sa_main、/system/bin/samgr 等&#xff09;&#xff0c;這些進程的 PPid…

龍虎榜——20250909

上證指數今天縮量收陰線&#xff0c;跌破10日均線&#xff0c;目前日線總體處于高位寬幅震蕩中&#xff0c;小級別暫未明確方向&#xff0c;指數面臨方向選擇&#xff0c;需要注意高位股的風險。 深證指數今天縮量收陰線&#xff0c;跌破5日均線&#xff0c;接下來幾天方向的選…

基于dijkstra算法的WSN網絡MAC協議matlab仿真,分析網絡延遲與網絡開銷

目錄 1.程序功能描述 2.測試軟件版本以及運行結果展示 3.部分程序 4.算法理論概述 5.完整程序 1.程序功能描述 無線傳感器網絡&#xff08;WSN, Wireless Sensor Network&#xff09;是由大量低成本、低功耗的傳感器節點組成&#xff0c;通過無線通信實現數據采集、傳輸與…

ES數據庫啟動時可以設置1G一下內存嗎

可以&#xff0c;但強烈不建議在生產環境中這樣做。ES 7.0 版本確實可以設置小于 1GB 的堆內存&#xff0c;但這會帶來嚴重的性能問題和穩定性風險。 快速回答 # 最小化配置示例 - 僅用于測試或開發環境 export ES_JAVA_OPTS"-Xms512m -Xmx512m" ./bin/elasticsearch…

TI-92 Plus計算器:單位換算功能介紹

1 TI-92 Plus計算器&#xff1a;單位換算功能介紹 TI-92 Plus 內置了全面的單位換算功能&#xff0c;支持長度、質量、時間、溫度、面積、體積、速度等數十種物理量的單位轉換&#xff0c;操作直觀&#xff0c;無需手動輸入換算系數。以下是具體使用方法、示例及功能特點&#…

雪球科技Java開發工程師筆試題

單選 1.下列哪些語句關于內存回收的說明是正確的?( C ) A.內存回收程序允許程序員直接釋放內存 B.程序員必須創建一個線程來釋放內存 C.內存回收程序負責釋放無用內存 D.內存回收程序可以在指定的時間釋放內存對象 2.以下哪項不是Java基礎類型(A) A.String B.int C.b…

NV3041A-01芯片屏幕

1. 核心概覽這是一款集成了電源管理、顯示內存&#xff08;RAM&#xff09;、時序控制等多種功能的單片顯示驅動芯片&#xff08;通常稱為Driver IC&#xff09;。它采用COG&#xff08;Chip-On-Glass&#xff09; 工藝&#xff0c;直接將芯片綁定在玻璃基板上&#xff0c;使得…

aiagent知識點

一、MCP (Model Context Protocol) 1. 核心概念是什么&#xff1a;MCP是一個開放協議&#xff0c;用于在應用&#xff08;如IDE、Agent&#xff09; 和工具/數據源&#xff08;如服務器、數據庫&#xff09; 之間建立標準化的通信。目標&#xff1a;解決AI工具生態的碎片化問題…

第2節-過濾表中的行-WHERE

摘要&#xff1a;在本教程中&#xff0c;您將學習如何使用 PostgreSQL 的 WHERE 子句來篩選表中的行。 PostgreSQL WHERE 子句 SELECT FROM 語句從表中所有行的一個或多個列中查詢數據。實際上&#xff0c;你經常需要選擇滿足某個條件的行。 要根據條件從表中篩選行&#xf…

IACheck賦能AI環評報告審核,推動環保設備制造行業發展

在“雙碳目標”和綠色制造的背景下&#xff0c;環保設備制造行業正在迎來快速發展。然而&#xff0c;環評報告作為項目合規的“通行證”&#xff0c;卻一直是企業最頭疼的環節之一&#xff1a;編寫復雜、審核周期長、錯誤率高。傳統的審核模式不僅耗時耗力&#xff0c;還容易出…

常見的多態

一、核心概念多態&#xff08;Polymorphism&#xff09; 的字面意思是“多種形態”。在Java中&#xff0c;它指的是&#xff1a;同一個行為&#xff08;方法&#xff09;具有多個不同表現形式或形態的能力。更具體地說&#xff0c;它允許你&#xff1a;父類的引用指向子類的對象…