一句話實現(xiàn)單例模式

如題

效果如下:

@interface AAA : NSObject <MySingleton>

@end

單例模式是開發(fā)中經(jīng)常會涉及到的開發(fā)結(jié)構(gòu)兢榨,實現(xiàn)單例模式的代碼大多千篇一律唾那。

網(wǎng)上也有很多大神系宜,簡化了單例的實現(xiàn)舌镶。大多數(shù)是用宏定義將需要寫的代碼進行了封裝喻喳,需要在.h文件中處理耻陕,也需要在.m文件中再處理袁串。懶人簡直不能忍??!倍权!

sunnyxx大神提到了一個通過__attribute__((objc_runtime_name("othername")))封裝的@singleton(Class, instanceMethod)的方法掷豺,受此啟發(fā),做了一個嘗試:

首先薄声,定義一個<單例協(xié)議>

@protocol MySingleton <NSObject>

@optional

+ (instancetype)sharedInstance;

@end

對于需要實現(xiàn)單例的類当船,則擴展此單例協(xié)議

然后,思路就是默辨,在系統(tǒng)加載之前德频,通過runtime方法,對擴展了此協(xié)議的類的allocWithZone:和copyWithZone:等方法進行添加替換:

@interface SingleObj : NSObject

@end

@implementation SingleObj

+ (void)load {

? ? int numClasses;

? ? Class*classes;

? ? classes =NULL;

? ? numClasses =objc_getClassList(NULL,0);

? ? if(numClasses >0) {

? ? ? ? classes = (Class*)malloc(sizeof(Class) * numClasses);

? ? ? ? numClasses =objc_getClassList(classes, numClasses);

? ? ? ? for(inti =0; i < numClasses; i++) {

? ? ? ? ? ? Class cls = classes[i];

? ? ? ? ? ? if (class_conformsToProtocol(cls, NSProtocolFromString(@"MySingleton"))) {

? ? ? ? ? ? ? ? //override allocWithZone:

? ? ? ? ? ? ? ? MethodclsMethod =class_getClassMethod(cls,@selector(allocWithZone:));

? ? ? ? ? ? ? ? id metaCls = objc_getMetaClass(NSStringFromClass(cls).UTF8String);

? ? ? ? ? ? ? ? if(clsMethod !=NULL) {

? ? ? ? ? ? ? ? ? ? Method method =class_getClassMethod(self,@selector(allocWithZone:));

? ? ? ? ? ? ? ? ? ? class_replaceMethod(metaCls,@selector(allocWithZone:),method_getImplementation(method),"@@:@");

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? //implementation sharedInstance

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? SEL SEL_sharedInstance =NSSelectorFromString(@"sharedInstance");

? ? ? ? ? ? ? ? ? ? Method Method_sharedInstance =class_getClassMethod(metaCls, SEL_sharedInstance);

? ? ? ? ? ? ? ? ? ? Method Method_sharedInstance_toApply =class_getClassMethod(self, SEL_sharedInstance);

? ? ? ? ? ? ? ? ? ? IMP IMP_sharedInstance =method_getImplementation(Method_sharedInstance_toApply);

? ? ? ? ? ? ? ? ? ? if(Method_sharedInstance ==NULL) {

? ? ? ? ? ? ? ? ? ? ? ? class_addMethod(metaCls, SEL_sharedInstance, IMP_sharedInstance,"@@:");

? ? ? ? ? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ? ? ? class_replaceMethod(metaCls, SEL_sharedInstance, IMP_sharedInstance,"@@:");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? //override copyWithZone:

? ? ? ? ? ? ? ? uintmethodCount =0;

? ? ? ? ? ? ? ? Method*methods =class_copyMethodList(cls, &methodCount);

? ? ? ? ? ? ? ? Method methodToApply =class_getClassMethod(self,@selector(copyWithZone:));

? ? ? ? ? ? ? ? IMP imp =method_getImplementation(methodToApply);

? ? ? ? ? ? ? ? BOOL copyWithZone =NO;

? ? ? ? ? ? ? ? for(inti =0; i < methodCount; i++) {

? ? ? ? ? ? ? ? ? ? Method method = methods[i];

? ? ? ? ? ? ? ? ? ? SEL sel =method_getName(method);

? ? ? ? ? ? ? ? ? ? if(sel ==@selector(copyWithZone:)) {

? ? ? ? ? ? ? ? ? ? ? ? class_replaceMethod(cls,@selector(copyWithZone:), imp,"@@:@");

? ? ? ? ? ? ? ? ? ? ? ? copyWithZone =YES;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? if(!copyWithZone) {

? ? ? ? ? ? ? ? ? ? class_addMethod(cls,@selector(copyWithZone:), imp,"@@:@");

? ? ? ? ? ? ? ? }

#if __has_feature(objc_arc)

#else

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? SEL sel = NSSelectorFromString(@"retain");

? ? ? ? ? ? ? ? ? ? Method method = class_getInstanceMethod(metaCls, sel);

? ? ? ? ? ? ? ? ? ? Method methodToApply = class_getInstanceMethod(self,@selector(returnSelf));

? ? ? ? ? ? ? ? ? ? IMP imp = method_getImplementation(methodToApply);

? ? ? ? ? ? ? ? ? ? if(method ==NULL) {

? ? ? ? ? ? ? ? ? ? ? ? class_addMethod(cls, sel, imp,"@@:");

? ? ? ? ? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ? ? ? class_replaceMethod(cls, sel, imp,"@@:");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? SEL sel = NSSelectorFromString(@"autorelease");

? ? ? ? ? ? ? ? ? ? Method method = class_getInstanceMethod(metaCls, sel);

? ? ? ? ? ? ? ? ? ? Method methodToApply = class_getInstanceMethod(self,@selector(returnSelf));

? ? ? ? ? ? ? ? ? ? IMP imp = method_getImplementation(methodToApply);

? ? ? ? ? ? ? ? ? ? if(method ==NULL) {

? ? ? ? ? ? ? ? ? ? ? ? class_addMethod(cls, sel, imp,"@@:");

? ? ? ? ? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ? ? ? class_replaceMethod(cls, sel, imp,"@@:");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? SEL sel = NSSelectorFromString(@"release");

? ? ? ? ? ? ? ? ? ? Method method = class_getInstanceMethod(metaCls, sel);

? ? ? ? ? ? ? ? ? ? Method methodToApply = class_getInstanceMethod(self,@selector(doNothing));

? ? ? ? ? ? ? ? ? ? IMP imp = method_getImplementation(methodToApply);

? ? ? ? ? ? ? ? ? ? if(method ==NULL) {

? ? ? ? ? ? ? ? ? ? ? ? class_addMethod(cls, sel, imp,"v@:");

? ? ? ? ? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ? ? ? class_replaceMethod(cls, sel, imp,"v@:");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? SEL sel = NSSelectorFromString(@"retainCount");

? ? ? ? ? ? ? ? ? ? Method method = class_getInstanceMethod(metaCls, sel);

? ? ? ? ? ? ? ? ? ? Method methodToApply = class_getInstanceMethod(self,@selector(maxCount));

? ? ? ? ? ? ? ? ? ? IMP imp = method_getImplementation(methodToApply);

? ? ? ? ? ? ? ? ? ? if(method ==NULL) {

? ? ? ? ? ? ? ? ? ? ? ? class_addMethod(cls, sel, imp,"i@:");

? ? ? ? ? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ? ? ? class_replaceMethod(cls, sel, imp,"i@:");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

#endif

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? free(classes);

? ? }

}

+ (instancetype)sharedInstance {

? ? return self.new;

}

+ (instancetype)allocWithZone:(NSZone*)zone {

? ? static id instance =nil;

? ? @synchronized (self) {

? ? ? ? if(!instance) {

? ? ? ? ? ? instance = [superallocWithZone:zone];

? ? ? ? }

? ? }

? ? returninstance;

}

- (instancetype)copyWithZone:(NSZone*)zone {

? ? return self;

}

#if __has_feature(objc_arc)

#else

- (void)doNothing {

}

- (instancetype)returnSelf {

? ? return self;

}

- (int)maxCount {

? ? returnINT_MAX;

}

#endif

@end

此方法優(yōu)點就是:幾乎沒有什么優(yōu)點??缩幸,就是寫法簡單壹置。

缺點:

1、需要在app啟動時做一些處理表谊,對效率有或多或少的影響(取決于具體的替換方法實現(xiàn))

2钞护、對實現(xiàn)了單例協(xié)議的Class的特定method進行了強制替換,導(dǎo)致需要實現(xiàn)的單例類對這幾個method的實現(xiàn)無效爆办,如果需要在alloc或者copy中做處理难咕,則不能應(yīng)用此法;當(dāng)然,也可以在替換時做檢測步藕,如果存在此method則不替換惦界,但是這就不能保證完全單例。(值得一提的是咙冗,檢測是否存在此方法時沾歪,不能用class_getInstanceMethod,此方法會查找父類)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雾消,一起剝皮案震驚了整個濱河市灾搏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌立润,老刑警劉巖狂窑,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異桑腮,居然都是意外死亡泉哈,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門破讨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丛晦,“玉大人,你說我怎么就攤上這事提陶√躺常” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵隙笆,是天一觀的道長锌蓄。 經(jīng)常有香客問我,道長撑柔,這世上最難降的妖魔是什么瘸爽? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮铅忿,結(jié)果婚禮上蝶糯,老公的妹妹穿的比我還像新娘。我一直安慰自己辆沦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布识虚。 她就那樣靜靜地躺著肢扯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪担锤。 梳的紋絲不亂的頭發(fā)上蔚晨,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機與錄音,去河邊找鬼铭腕。 笑死银择,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的累舷。 我是一名探鬼主播浩考,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼被盈!你這毒婦竟也來了析孽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤只怎,失蹤者是張志新(化名)和其女友劉穎袜瞬,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體身堡,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡邓尤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贴谎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汞扎。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赴精,靈堂內(nèi)的尸體忽然破棺而出佩捞,到底是詐尸還是另有隱情,我是刑警寧澤蕾哟,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布一忱,位于F島的核電站,受9級特大地震影響谭确,放射性物質(zhì)發(fā)生泄漏帘营。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一逐哈、第九天 我趴在偏房一處隱蔽的房頂上張望芬迄。 院中可真熱鬧,春花似錦昂秃、人聲如沸禀梳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽算途。三九已至,卻和暖如春蚀腿,著一層夾襖步出監(jiān)牢的瞬間嘴瓤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留廓脆,地道東北人筛谚。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像停忿,于是被迫代替她去往敵國和親驾讲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361