-
前言 单例模式基本上是最简单的设计模式,也叫单子模式,是一种常用的软件设计模式。 在应用这个模式时,单例对象的类必须保证只有一个实例存在。 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。
-
基础款
#import "Singleton.h"
@implementation Singleton
+ (Singleton *)sharedInstance
{
static Singleton *sharedSingleton = nil;
if (sharedSingleton) {
sharedSingleton = [[Singleton alloc] init];
}
return sharedSingleton;
}
@end
基本上是最简单的单例创建形式,首先检查唯一实例是否已经创建,若没有则创建新的实例并且返回,考虑到多线程等因素,这不够好。
- 多线程款
+ (Singleton *)sharedInstance
{
static Singleton *sharedSingleton = nil;
static dispatch_once_t token;
dispatch_once(&token, ^{
sharedSingleton = [[Singleton alloc] init];
});
return sharedSingleton;
}
dispatch_once
该方法的作用就是执行且在整个程序的声明周期中,仅执行一次某一个block对象,满足线程要求。
- 更严谨款
然而,我们直接调用
[[sharedInstance alloc] init]
方法显然并不能保证放回对象的同一个实例。我们需要隔绝各种导致不返回同一实例的写法。苹果提供了复杂的标准写法 传送门
+ (Singleton *)sharedInstance
{
static Singleton *sharedSingleton = nil;
static dispatch_once_t token;
dispatch_once(&token, ^{
sharedSingleton = [[super allocWithZone:NULL] init];
});
return sharedSingleton;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
// mrc
- (id)retain
{
return self;
}
// mrc
- (NSUInteger)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
// mrc
- (void)release
{
//do nothing
}
// mrc
- (id)autorelease
{
return self;
}
为什么 sharedSingleton = [[super allocWithZone:NULL] init];
用的是super,而不是self? 因为我们已经重载了allocWithZone
的对象分配方法,所以我们需要借助父类来实现内存分配。
- 子类化单例款
然而,由于我们重写了单例 的
allocWithZone
方法 ,并且把内存分配交给了super
,那么我们不做修改的子类化单例,返回的实例始终将会是Singleton
而不是Singleton
的子类对象。 那么我们要怎样子类化单例呢?我们知道为对象创建分配内存的是alloc
方法,我们得看看alloc
方法是怎样实现的
alloc:
+ (id)alloc {
return _objc_rootAlloc(self);
}
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
我们注意到官方源码_objc_rootAlloc
之上的注释
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
我们首先注意到callAlloc
中的注释No alloc/allocWithZone implementation. Go straight to the allocator.
,如果没有类没有实现alloc或者allocWithZone方法才会执行#if __OBJC2__
和#endif
之间的逻辑,否则执行if (allocWithZone) return [cls allocWithZone:nil];
该语句
其次 _objc_rootAlloc
方法的注释 cls is not nil. Calls [cls allocWithZone:nil]
,如果cls不为空,则调用allocWithZone方法
显然,大部分情况下alloc
直接调用的就是allocWithZone
方法
** allocWithZone**:
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;
#if __OBJC2__
// allocWithZone under __OBJC2__ ignores the zone parameter
(void)zone;
obj = class_createInstance(cls, 0);
#else
if (!zone) {
obj = class_createInstance(cls, 0);
}
else {
obj = class_createInstanceFromZone(cls, 0, zone);
}
#endif
if (slowpath(!obj)) obj = callBadAllocHandler(cls);
return obj;
}
allocWithZone 就比较简单了,在objc2下直接调用的是class_createInstance
方法
** class_createInstance **
这个方法处于<objc/runtime.h>
/* Instantiating Classes */
/**
* Creates an instance of a class, allocating memory for the class in the
* default malloc memory zone.
*
* @param cls The class that you wish to allocate an instance of.
* @param extraBytes An integer indicating the number of extra bytes to allocate.
* The additional bytes can be used to store additional instance variables beyond
* those defined in the class definition.
*
* @return An instance of the class \e cls.
*/
OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
然而OBJC_ARC_UNAVAILABLE
arc下不可用,不过没关系-fno-objc-arc
来支持混编。
所以,最后的最后,支持子类化的单例就写好了
+ (Singleton *)sharedInstance
{
static Singleton *sharedSingleton = nil;
static dispatch_once_t token;
dispatch_once(&token, ^{
// sharedSingleton = [[super allocWithZone:NULL] init];
sharedSingleton = [class_createInstance([self class], 0) init ];
});
return sharedSingleton;
}
That is all, thank you