iOS底層原理 - 單例的正確寫法

面試題引發(fā)的思考:

Q: 介紹單例及其用途鬼廓?

  • 單例模式保證系統(tǒng)中 一個類 只有 一個實例 而且該實例 易于外界訪問援所。
  • 只初始化一次蝙云,生命周期 和 程序的生命周期 相同酸员,訪問方便;
  • 主要用來封裝網(wǎng)絡(luò)請求河胎、播放器闯袒、存放常用數(shù)據(jù)等。

Q: 單例正確寫法游岳?

  • OC正確寫法如下:
// TODO: -----------------  Singleton類  -----------------
@interface Singleton : NSObject <NSCopying, NSMutableCopying>
+ (instancetype)sharedInstance;
- (void)justdoit;
@end

@implementation Singleton
// 初始化方法dispatch_once政敢,本身是線程安全的,保證整個程序中只會執(zhí)行一次
+ (instancetype)sharedInstance  {
    static Singleton *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[super allocWithZone:nil] init];
    });
    return instance;
}
// 重寫 allocWithZone
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [Singleton sharedInstance];
}
// 遵從NSCopying協(xié)議胚迫,可通過copy方式創(chuàng)建對象
- (id)copyWithZone:(NSZone *)zone {
    return self;
}
// 遵從NSMutableCopying協(xié)議堕仔,可通過mutableCopy方式創(chuàng)建對象
- (id)mutableCopyWithZone:(NSZone *)zone {
    return self;
}
// 實例方法
- (void)justdoit {
    NSLog(@"just do it");
}
@end
  • Swift正確寫法如下:
// TODO: -----------------  Singleton類  -----------------
class Singleton {
    static let shared = Singleton()
    // 私有化構(gòu)造器
    private init() { }
    // 方法
    func justdoit() {
        print("just do it")
    }
}

1. 單例

(1) 單例模式

單例模式保證系統(tǒng)中 一個類 只有 一個實例 而且該實例 易于外界訪問

  • 只初始化一次晌区,生命周期 和 程序的生命周期 相同摩骨,訪問方便;
  • 主要用來封裝網(wǎng)絡(luò)請求朗若、播放器恼五、存放常用數(shù)據(jù)等。

經(jīng)常見到的一些系統(tǒng)單例:

[UIApplication sharedApplication]:應(yīng)用程序
[NSNotificationCenter defaultCenter]:通知
[NSUserDefaults standardUserDefaults]:本地化存儲
[NSFileManager defaultManager]:文件操作


(2) 單例寫法

蘋果官方推薦寫法:

// TODO: -----------------  Singleton類  -----------------
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
- (void)justdoit;
@end

@implementation Singleton
+ (instancetype)sharedInstance {
    // 聲明一個 空的靜態(tài)的 單例對象
    static Singleton *instance = nil;
    // 聲明一個 靜態(tài)的 gcd的單次任務(wù)
    static dispatch_once_t onceToken;
    // 執(zhí)行g(shù)cd單次任務(wù)
    dispatch_once(&onceToken, ^{
        // 對對象進行初始化
        instance = [[Singleton alloc] init];
    });
    return instance;
}
- (void)justdoit {
    NSLog(@"just do it");
}
@end

通過[Singleton sharedInstance]創(chuàng)建對象哭懈,即可調(diào)用相關(guān)方法:

// TODO: -----------------  ViewController類  -----------------
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    /// 創(chuàng)建單例
    Singleton *singleton = [Singleton sharedInstance];
    [singleton justdoit];
}
@end

// 打印結(jié)果
Demo[1234:567890] just do it

(3) 以上寫法問題

如果不通過[Singleton sharedInstance]創(chuàng)建對象灾馒,而是通過alloc或者new創(chuàng)建對象,會出現(xiàn)什么問題呢遣总?

// TODO: -----------------  ViewController類  -----------------
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    /// 通過不同方式創(chuàng)建對象
    Singleton *singletonA= [Singleton sharedInstance];
    Singleton *singletonB= [Singleton sharedInstance];
    Singleton *singletonC = [[Singleton alloc] init];
    Singleton *singletonD = [Singleton new];

    NSLog(@"singletonA - %@", singletonA);
    NSLog(@"singletonB - %@", singletonB);
    NSLog(@"singletonC - %@", singletonC);
    NSLog(@"singletonD - %@", singletonD);
}
@end

// 打印結(jié)果
Demo[1234:567890] singletonA - <Singleton: 0x283d35290>
Demo[1234:567890] singletonB - <Singleton: 0x283d35290>
Demo[1234:567890] singletonC - <Singleton: 0x283d352a0>
Demo[1234:567890] singletonD - <Singleton: 0x283d352b0>

由以上結(jié)果可知:

實例對象singletonAsingletonB的地址值是相同的睬罗;而此兩者與實例對象singletonCsingletonD的地址值是不同的轨功。

說明此情況下:

通過[Singleton sharedInstance]只會創(chuàng)建一次對象;而通過alloc或者new會創(chuàng)建新的對象容达。

因為單例模式保證系統(tǒng)中一個類只有一個實例古涧,所以以上單例的寫法不夠嚴謹,會出現(xiàn)問題花盐。


2. 單例的正確寫法

(1) 正確寫法一

在創(chuàng)建對象的時候羡滑,alloc或者new都會調(diào)用到allocWithZone:方法,需要重寫 allocWithZone:方法算芯。

如果調(diào)用了copymutableCopy方法就會導(dǎo)致程序運行崩潰柒昏,需要實現(xiàn)copymutableCopy就要遵循協(xié)議實現(xiàn)方法。

// TODO: -----------------  Singleton類  -----------------
@interface Singleton : NSObject <NSCopying, NSMutableCopying>
+ (instancetype)sharedInstance;
- (void)justdoit;
@end

@implementation Singleton
// 初始化方法dispatch_once熙揍,本身是線程安全的职祷,保證整個程序中只會執(zhí)行一次
+ (instancetype)sharedInstance  {
    static Singleton *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[super allocWithZone:nil] init];
    });
    return instance;
}
// 重寫 allocWithZone
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [Singleton sharedInstance];
}
// 遵從NSCopying協(xié)議,可通過copy方式創(chuàng)建對象
- (id)copyWithZone:(NSZone *)zone {
    return self;
}
// 遵從NSMutableCopying協(xié)議届囚,可通過mutableCopy方式創(chuàng)建對象
- (id)mutableCopyWithZone:(NSZone *)zone {
    return self;
}
// 實例方法
- (void)justdoit {
    NSLog(@"just do it");
}
@end

通過各種方式創(chuàng)建對象有梆,查看打印結(jié)果:

// TODO: -----------------  ViewController類  -----------------
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    Singleton *singletonA= [Singleton sharedInstance];
    Singleton *singletonB= [Singleton sharedInstance];
    Singleton *singletonC = [[Singleton alloc] init];
    Singleton *singletonD = [Singleton new];
    Singleton *singletonE = [singletonA copy];
    Singleton *singletonF = [singletonC mutableCopy];

    NSLog(@"singletonA - %@", singletonA);
    NSLog(@"singletonB - %@", singletonB);
    NSLog(@"singletonC - %@", singletonC);
    NSLog(@"singletonD - %@", singletonD);
    NSLog(@"singletonE - %@", singletonE);
    NSLog(@"singletonF - %@", singletonF);
}
@end

// 打印結(jié)果
Demo[1234:567890] singletonA - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonB - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonC - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonD - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonE - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonF - <Singleton: 0x2815b9310>

由打印結(jié)果可知:

實例對象singletonA~singletonF的地址值是相同的。

說明此情況下:

通過各種方式創(chuàng)建對象奖亚,而對象只會創(chuàng)建一次,符合單例模式定義析砸。


(2) 正確寫法二

設(shè)置只能通過[Singleton sharedInstance]創(chuàng)建對象昔字,直接禁用allocnew首繁、copy作郭、mutableCopy等方法即可:

// TODO: -----------------  Singleton類  -----------------
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
- (void)justdoit;

+ (instancetype)alloc NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)copy NS_UNAVAILABLE;
- (instancetype)mutableCopy NS_UNAVAILABLE;
@end

@implementation Singleton
+ (instancetype)sharedInstance {
    static Singleton *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[Singleton alloc] init];
    });
    return instance;
}
- (void)justdoit {
    NSLog(@"just do it");
}
@end

此時無法通過allocnew弦疮、copy夹攒、mutableCopy等方法創(chuàng)建對象,調(diào)用會報錯:

報錯

(3) Swift單例正確寫法

Swift中創(chuàng)建單例很方便:

// TODO: -----------------  Singleton類  -----------------
class Singleton {
    static let shared = Singleton()
    // 私有化構(gòu)造器
    private init() { }

    func justdoit() {
        print("just do it")
    }
}

// TODO: -----------------  ViewController類  -----------------
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any setup after loading the view.
        let singleton = Singleton.shared
        singleton.justdoit()
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胁塞,一起剝皮案震驚了整個濱河市咏尝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌啸罢,老刑警劉巖编检,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扰才,居然都是意外死亡允懂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門衩匣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蕾总,“玉大人粥航,你說我怎么就攤上這事∩伲” “怎么了递雀?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長置侍。 經(jīng)常有香客問我映之,道長,這世上最難降的妖魔是什么蜡坊? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任杠输,我火速辦了婚禮,結(jié)果婚禮上秕衙,老公的妹妹穿的比我還像新娘蠢甲。我一直安慰自己,他們只是感情好据忘,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布鹦牛。 她就那樣靜靜地躺著,像睡著了一般勇吊。 火紅的嫁衣襯著肌膚如雪曼追。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天汉规,我揣著相機與錄音礼殊,去河邊找鬼。 笑死针史,一個胖子當著我的面吹牛晶伦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播啄枕,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼婚陪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了频祝?” 一聲冷哼從身側(cè)響起泌参,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎常空,沒想到半個月后及舍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡窟绷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年锯玛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡攘残,死狀恐怖拙友,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歼郭,我是刑警寧澤遗契,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站病曾,受9級特大地震影響牍蜂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泰涂,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一鲫竞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逼蒙,春花似錦从绘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驳棱,卻和暖如春批什,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背社搅。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工驻债, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罚渐。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓却汉,卻偏偏與公主長得像驯妄,于是被迫代替她去往敵國和親荷并。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 單例應(yīng)該是iOS中很簡單的設(shè)計模式青扔,寫個單例很簡單很方便源织。網(wǎng)上例子也很多,大家也是基本上copy下來就可以了...
    楓葉無處漂泊閱讀 5,625評論 0 12
  • 前言單例模式基本上是最簡單的設(shè)計模式微猖,它使類的一個對象成為整個系統(tǒng)的唯一實例谈息。 基礎(chǔ)款 基本上是最簡單的單例創(chuàng)建形...
    madaoCN閱讀 1,526評論 3 7
  • 常規(guī)不遵從NSCopying代理,不重寫allocWithZone方法會導(dǎo)致用[class allow]init]...
    _亂閱讀 535評論 0 0
  • 單例模式大概是設(shè)計模式種較簡單的一種設(shè)計模式凛剥。但在實際的開發(fā)過程中仍然存在一些坑侠仇。所以本文總結(jié)了下iOS中的單例模...
    CrazyItCoder閱讀 1,316評論 0 4
  • 一、OC 中創(chuàng)建單例的幾種方式 1.1 單線程模式單例 存在的問題就是: 只能在單線程中使用, 一旦有多線程同時調(diào)...
    下班不寫程序閱讀 1,157評論 0 4