如題
效果如下:
@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,此方法會查找父類)