iOS開發筆記之八十——單例的總結筆記

******閱讀完此文,大概需要10分鐘******

一、單例的創建

#import "MDInstanceManager.h"
 
@implementation MDInstanceManager
 
static MDInstanceManager *shareInstance = nil;
static dispatch_once_t onceToken;
 
+ (instancetype)shareInstance
{
    dispatch_once(&onceToken, ^{
        shareInstance = [[self alloc] init];
    });
    return shareInstance;
}
  
- (instancetype)init
{
    self = [super init];
    if (self) {}
    return self;
}
@end

單例的關鍵字static,被它修飾的對象,存儲在靜態存儲區,會在當前內存中保持唯一性和持久性(對象會在程序的整個運行期間都存在);上面的單例寫法是最爲常見的做法,dispatch_once保證了線程安全性,但是如果要保證真正的唯一性,還不夠。

二、“嚴格”的單例

+ (id)allocWithZone:(NSZone *)zone
{
    if(!shareInstance) {
        shareInstance = [super allocWithZone:zone];
    }
    return shareInstance;
}
 
- (id)copy
{
    return shareInstance;
}
 
- (id)mutableCopy
{
    return shareInstance;
}

創建對象的步驟分爲兩步:(1)申請內存(alloc);(2)初始化(init) 這兩個步驟;

因爲外部的操作是不可控的,爲了保證嚴格的唯一性,因此在第一步這個階段我們就要攔截它;當我們調用alloc方法時,OC內部會調用allocWithZone這個方法來申請內存,我們覆寫這個方法,然後在這個方法中調用shareInstance方法返回單例對象,這樣就可以達到我們的目的。

拷貝對象也是同樣的原理,覆寫copy方法,然後在這個方法中調用shareInstance方法返回單例對象。

這樣以來:

MDInstanceManager *instanceManager = [MDInstanceManager shareInstance];
NSLog(@"-->%@",instanceManager);
NSLog(@"-->%@",[[MDInstanceManager alloc]init]);
NSLog(@"-->%@",[instanceManager copy]);
NSLog(@"-->%@",[instanceManager mutableCopy]);
2020-06-30 11:21:53.886952+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>
2020-06-30 11:21:53.887055+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>
2020-06-30 11:21:56.200656+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>
2020-06-30 11:21:56.840126+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>

以上四種操作的內存地址都是一樣的;

當然你也可以採取更爲粗暴的方法,如下:

- (instancetype)init __attribute__((unavailable("replace with 'sharedInstance'")));
+ (instancetype)alloc __attribute__((unavailable("replace with 'sharedInstance'")));
+ (instancetype)new __attribute__((unavailable("replace with 'sharedInstance'")));
- (instancetype)copy __attribute__((unavailable("replace with 'sharedInstance'")));
- (instancetype)mutableCopy __attribute__((unavailable("replace with 'sharedInstance'")));

三、單例優缺點

優點:

  • 提供了應用唯一的實例對象,規範化統一管理資源,即提供了對唯一實例的受控訪問; 

  • 不用再頻繁地創建和銷燬對象,從而提高了系統的性能和節約系統資源;

  • 單例對象可以做到按需創建對象或加載資源,以節省不必要的內存;

  • 避免對共享資源的多重佔用;

缺點:

  • 單例從創建後到徹底關閉程序前都會一直存在,如果過多的創建單例無疑浪費系統資源和影響系統效率;

  • 由於單例模式中沒有抽象層接口,因此單例類很難再進行擴展;

  • 單例類的職責過重,在一定程度上違背了“單一職責原則”,長期的累積會導致難以維護;

  • 因此可以把單例模式作爲最後的兜底方案考慮,不宜過多使用;

四、單例的銷燬

單例是可以銷燬的,如下:

- (void)destroy
{
    shareInstance = nil;
    onceToken = 0;
}

五、Weak單例


static MDInstanceManager *shareInstance = nil;
以上static修飾的默認用一個強指針來持有這個單例,如果當前業務場景退出後,對應的單例管理對象,也沒有用了,當然,也可以手動釋放這個單例對象,但是業務使用中,需要注意的是釋放的時機;
static __weak MDWeakInstanceManager *weakInstance = nil;
但是,如果改用OC特有的weak修飾,讓對應的VC去持有它,那麼這個weak單例就會自動伴隨對應的VC釋放而釋放,而不需要我們去手動在干預;對於複雜的業務場景,我們常常需要一個管理類來作爲中間者,weak單例無疑是一種很好的選擇。

爲了規範化這個過程,在實際開發中,我定義了一個protocol才實現VC對weak單例的持有,讓對應的持有者去實現它,如下:
@protocol MDWeakInstanceManagerDelegate <NSObject>
 
- (void)assignInstance:(MDWeakInstanceManager *)instance;
 
@end

持有者VC的實現代碼如下:

@interface MDWeakInstanceViewController ()<MDWeakInstanceManagerDelegate>
 
@property (nonatomic, strong) MDWeakInstanceManager *weakInstanceManager;
  
@end
  
@implementation MDWeakInstanceViewController
  
- (void)viewDidLoad
{
    [super viewDidLoad];
    [MDWeakInstanceManager buildInstance:self];
}
  
#pragma mark -- MDWeakInstanceManagerDelegate
 
- (void)assignInstance:(MDWeakInstanceManager *)instance
{
    self.weakInstanceManager = instance;
}
  
@end

單例中實現如下:

static __weak MDWeakInstanceManager *weakInstance = nil;
  
+ (void)buildInstance:(id)delegate;
{
    MDWeakInstanceManager *strongInstance = weakInstance;
    @synchronized(self) {
        if (!strongInstance) {
            strongInstance = [[[self class] alloc] init];
            weakInstance = strongInstance;
        }
    }
    strongInstance.delegate = delegate;
    if (strongInstance.delegate && [strongInstance.delegate respondsToSelector:@selector(assignInstance:)]) {
        [strongInstance.delegate assignInstance:strongInstance];
    }
}
 
//訪問時須用此方法
+ (MDWeakInstanceManager *)shareInstance
{
    return weakInstance;
}

這樣我們在持有者VC的業務場景中,都可以使用 [MDWeakInstanceManager shareInstance],而一旦它的持有者VC釋放了,[MDWeakInstanceManager shareInstance]也會自動變爲nil,無需手動干涉;

 

參考文獻

https://www.cnblogs.com/NerdFooProgrammer/p/4870260.html

https://www.yunbook.vip/post/1547606575302.html

http://www.cocoachina.com/articles/19857

 

請關注公衆號:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章