在做zon项目中,单例都是:
@implementation AppShareDataManager static AppShareDataManager * shareDataManager = nil; @synthesize theCurrentLanguage; @synthesize presentModalFlag; .......... +(AppShareDataManager *) sharedManager { if( shareDataManager == nil ) { shareDataManager = [ [ AppShareDataManager alloc ] init ]; } return shareDataManager; } -(id)init{ self = [super init]; if(self){ //对象实例初始化 theCurrentLanguage = [ZONUserDefaultManager getAppDefaultLanguage]; ........ } return self; }
调试发现
AppShareDataManager *A = [[AppShareDataManager alloc] init];
NSLog(@"A:%@",A);
AppShareDataManager *B = [AppShareDataManager sharedManager];
NSLog(@"B:%@",B);
打印出的是
2013-02-28 23:27:25.368 ZON2012[10647:c07] A:<AppShareDataManager: 0x12638630>
2013-02-28 23:27:25.369 ZON2012[10647:c07] B:<AppShareDataManager: 0xb584b40>
不是一个内存地址,也就是不是同一个实体!
这是apple官方的一个单例建议:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32
大致如下:
/* Singleton.h */ #import <Foundation/Foundation.h> @interface Singleton : NSObject + (Singleton *)instance; @end /* Singleton.m */ #import "Singleton.h" static Singleton *instance = nil; @implementation Singleton + (Singleton *)instance { if (!instance) { instance = [[super allocWithZone:NULL] init]; } return instance; } + (id)allocWithZone:(NSZone *)zone { return [self instance]; } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)init { if (instance) { return instance; } self = [super init]; return self; } - (id)retain { return self; } - (oneway void)release { // Do nothing } - (id)autorelease { return self; } - (NSUInteger)retainCount { return NSUIntegerMax; } @end
这是一种很标准的Singleton实现,中规中矩。不过这种实现并不是线程安全的。所以各路大神都各显神威,给出了多种单例模式的实现。
现在把他加强下:
static MyClass *class = nil; @implementation MyClass +(MyClass *)sharedMyClass{ @synchronized(self){ //为了确保多线程情况下,仍然确保实体的唯一性 if (!class) { [[self alloc] init]; //该方法会调用 allocWithZone } } return class; } +(id)allocWithZone:(NSZone *)zone{ @synchronized(self){// //为了确保多线程情况下,仍然确保实体的唯一性 if (!class) { class = [super allocWithZone:zone]; //确保使用同一块内存地址 return class; } } return nil; } -(id)init { if(class){ return class; } if(self = [super init]){ //进行一些初始化 } return self ; } - (id)copyWithZone:(NSZone *)zone;{ return self; //确保copy对象也是唯一 } -(id)retain{ return self; //确保计数唯一 } - (unsigned)retainCount { return UINT_MAX; //装逼用的,这样打印出来的计数永远为-1 } - (id)autorelease { return self;//确保计数唯一 } - (oneway void)release { //重写计数释放方法 do nothing } @end
再调试
MyClass *A = [[MyClass alloc] init];
NSLog(@"A:%@",A);
MyClass *B = [MyClass sharedMyClass];
NSLog(@"B:%@",B);
MyClass *C = [A copy];
NSLog(@"C:%@",C);
打印出的是
A:<MyClass: 0x6a1e130>
B:<MyClass: 0x6a1e130>
C:<MyClass: 0x6a1e130>
都是指向同一块内存地址
-----------------------------------切糕分割线--------------------
然而这个人(http://eschatologist.net/blog/?p=178)觉的繁琐,所以给出如下实现:
@interface SomeManager : NSObject + (id)sharedManager; @end /* 非线程安全的实现 */ @implementation SomeManager + (id)sharedManager { static id sharedManager = nil; if (sharedManager == nil) { sharedManager = [[self alloc] init]; } return sharedManager; } @end /* 线程安全的实现 */ @implementation SomeManager static id sharedManager = nil; + (void)initialize { if (self == [SomeManager class]) { sharedManager = [[self alloc] init]; } } + (id)sharedManager { return sharedManager; } @end
-----------------------------------切糕分割线--------------------
自苹果引入了Grand Central Dispatch (GCD)(Mac OS 10.6和iOS4.0)后,利用GCD(Grand Central Dispatch)和ARC(Automatic Reference Counting)实现单例。
+(SchoolManager *)sharedInstance { __strong static SchoolManager *sharedManager; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedManager = [[SchoolManager alloc] init]; }); return sharedManager; }
函数void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);其中第一个参数predicate,该参数是检查后面第二个参数所代表的代码块是否被调用的谓词,第二个参数则是在整个应用程序中只会被调用一次的代码块。dispach_once函数中的代码块只会被执行一次,而且还是线程安全的。
看到如下一篇文章,用宏实现(https://gist.github.com/lukeredpath/1057420):
ExampleClass.m
GCDSingleton.h
/*!
* @function Singleton GCD Macro
*/
#ifndef SINGLETON_GCD
#define SINGLETON_GCD(classname) \
\
+ (classname *)shared##classname { \
\
static dispatch_once_t pred; \
__strong static classname * shared##classname = nil;\
dispatch_once( &pred, ^{ \
shared##classname = [[self alloc] init]; }); \
return shared##classname; \
}
#endif
This assumes the init is a standard init.
Then to implement in the .h
@interface MyClass : NSObject
+ (MyClass *) sharedMyClass;
@end
and in the .m
#import "MyClass.h"
@implementation MyClass
SINGLETON_GCD(MyClass);
- (id) init {
if ( (self = [super init]) ) {
// Initialization code here.
}
return self;
}
@end
I assume that __strong is not necessary if this is a Mac OS X app?
Also, allocWithZone can be ignored when using Garbage Collection?
Thanks again...
// SynthesizeSingleton.h
// CocoaWithLove
//
// Created by Matt Gallagher on 20/10/08.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
\
static classname *shared##classname = nil; \
\
+ (classname *)shared##classname \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [[self alloc] init]; \
} \
} \
\
return shared##classname; \
} \
\
+ (id)allocWithZone:(NSZone *)zone \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [super allocWithZone:zone]; \
return shared##classname; \
} \
} \
\
return nil; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return self; \
} \
\
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return NSUIntegerMax; \
} \
\
- (void)release \
{ \
} \
\
- (id)autorelease \
{ \
return self; \
}
id分配的几种方式
方式一:单点自增分配。全局由一个模块来负责生成id,可保证id从0开始连续递增,数据一般放在本地文件。简洁,但致命的问题是单点故障会导致服务整体不可用。
方式一改进:为该模块提供主从复制的能力,或者干脆将数据放在mysql里,利用mysql的主从复制,都一定程度上增强了可用性,减轻了单点故障的影响。
方式二:随机/散列分配。通过一些hash算法,比如以时间+随机串为key的md5生成一个唯一的id,关键点在于算法和key的选择要避免冲突。
最典型的就是UUID,UUID的标准型式包含32个16进位数字,以连字号分为五段,形式为8-4-4-4-12的32个字符,如550e8400-e29b-41d4-a716-446655440000。libuuid提供了以时间或者随机数为基的UUID。UUID的最大缺点是位数太长,128位,在绝大多数应用和语言里对128位整数的支持都不好。
方式二改进:有条件的进行压缩。twitter的snowflake使用 time – 41 bits + configured machine id – 10 bits + sequence number – 12 bits的形式分配id,共63位,最高部分使用毫秒级的时间戳,保证了一定程度的有序性,机器标示使用10位,最多可容纳1024个分配器,最后的12位序列号可以支持在1ms内产生4096个不重复的id。从工程角度,这些都足够用了。但对系统时间的依赖性非常强,需要关闭ntp的时间同步功能,或者当检测到ntp时间调整后,拒绝分配id。
我们的需求和多IDC的挑战
我们的实际情况是:
· 一些老模块依赖于从0开始自增的id,数据在内存或者文件中以id为偏移来存储的。
· 一些系统依赖于id的增长做数据分片,例如按取除后分表,因此要求id在整体上是比较均衡的增长。
· 在多IDC环境,高延迟加不稳定的网络环境,要求各个分配器彼此之间无需协作,或者可以容忍短期内不可协作。
· 对于一些古董级的老系统来说,还在使用32位的id,63位id还是太大了。
因此,我们需要一种分布式高可用、从0开始自增、基本均衡、能够兼容老系统的id分配方案。
取模或分段的分布式分配
基于方案一再改进一步,将整个id空间按取模或分段等分为若干个独立的id子空间,每个id子空间由一个独立的分配器负责。
优点:简单,各个id分配器无需协作,即使发生网络划分时,也可保证可用性和id的不冲突。
如果在国际化环境的多IDC里进行部署,需要预先将id空间划分为N份,每个国家里部署若干份。每个IDC内应用只连本IDC的id分配服务。
在均衡性上的不足:在同一个IDC内,均衡性可以在接入层均衡算法保证,但是在多个IDC里,ID分配器个数的比例和id增长的服务往往是不吻合的,因此在多个IDC内,id是无法保证均衡增长的。
均衡性上的改进
将id分配分为两层:
· 上层的“id分配器”对应用暴露,提供一次申请一个id的接口,一般本IDC的应用只连本IDC的id分配器。
·下层的“段分配器”对“id分配器”提供服务。id分配器“知晓”所有IDC的所有段分配器的存在,使用均衡策略向段分配器申请一个id段,当所持有的id段快耗尽时,再请求下一个段。
唯一性:全局中,根据分片规则,每个段分配器会持有不同的id段。例如下表中,每个段的大小是100,段分配器A持有分片0和分片1。对于每个分片而言,是一个个跳跃的id段。特殊的,当段大小为1时,段分配器就是改进前的id分配器。
均衡策略:均衡策略在id分配器来实现,简单的讲,是一个轮询策略。每个id分配器会轮询下游段分配器的状态,并选中id段的最小的那个,然后发起id段申请。由于不会加锁,当多个id分配器同时竞争时,可能会出现获取的id段不是全局最小的,可以附加一些策略来调优,比如再多获取一次,并本地排序。从整体上而言,id还是比较均衡的,可满足需求。
可用性:当发生网络划分时,本IDC的id分配器可以只连接本IDC的段分配器,成功的申请到id段。整个系统可容忍一定时间内不可协作,长时间不可协作的唯一危害是id增长不均衡,此时,就退化为改进前的方案。
多IDC环境的适应性:id分配器需要和所有IDC的段分配器交互,但是交互频率很低,同时和提供id分配服务是两个独立的阶段,不会受到多IDC网络环境的干扰。
效果
改进后的id分配方案成功的满足了图片系统重构过程中的兼容需求,并且部署在全球多个IDC内为图片系统提供全局唯一的id分配服务。
第一步:添加界面,并给文本框设置标签:input 如下图:
[img]
[/img]
第二步:选中+号按钮,在交互界面选择onClic(点击时),进入界面:
[img]
[/img]
选择界面上的编辑条件,如下图所示:
[img]
[/img]
然后选择第2步:添加动作 中的 部件与变量中的 设置变量/部件值,设置成如下图所示:
[img]
[/img]
添加完条件与动作后,选中+号按钮,交互界面如下:
[img]
[/img]
第三步(-号按钮)与第二步大同小异,只是条件稍微修改下,改为:大于等于1;input控件的值改为:
[[number-1]];
条件:
[img]
[/img]
动作:
[img]
[/img]