如题
效果如下:
@interface AAA : NSObject <MySingleton>
@end
单例模式是开发中经常会涉及到的开发结构,实现单例模式的代码大多千篇一律。
网上也有很多大神,简化了单例的实现。大多数是用宏定义将需要写的代码进行了封装,需要在.h文件中处理,也需要在.m文件中再处理。懒人简直不能忍!!
sunnyxx大神提到了一个通过__attribute__((objc_runtime_name("othername")))封装的@singleton(Class, instanceMethod)的方法,受此启发,做了一个尝试:
首先,定义一个<单例协议>
@protocol MySingleton
@optional
+ (instancetype)sharedInstance;
@end
对于需要实现单例的类,则扩展此单例协议
然后,思路就是,在系统加载之前,通过runtime方法,对扩展了此协议的类的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
此方法优点就是:几乎没有什么优点,就是写法简单。
缺点:
1、需要在app启动时做一些处理,对效率有或多或少的影响(取决于具体的替换方法实现)
2、对实现了单例协议的Class的特定method进行了强制替换,导致需要实现的单例类对这几个method的实现无效,如果需要在alloc或者copy中做处理,则不能应用此法;当然,也可以在替换时做检测,如果存在此method则不替换,但是这就不能保证完全单例。(值得一提的是,检测是否存在此方法时,不能用class_getInstanceMethod,此方法会查找父类)