单例

ps:所谓单例,就是在程序中只有一个实例,它提供一个类方法供全局调用。在编译时初始化这个类,然后保存在内存中,在程序退出时,由系统自动释放这部分内存。系统为我们提供的单例有:UIApplication、NSNotificationCenter、NSFileManager、NSUserDefaults、NSURLCache、NSHTTPCookieStorage等。

创建一个单例时,需要注意的是:实例化一个类有多种方法,alloc、new、allocWithZone,你写的初始化单例的类方法 sharedInstance,但你不能保证你的同伴不会调用其他初始化方法去初始化这个类,因此你需要将其余的初始化方法全部干掉,保证不管用什么初始化方法创建的实例,都有一个共同的内存地址。

+(instancetype)sharedInstance
{
    static Person *p = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        p = [[Person alloc]init];
    });
    return p;
}

    Person *p1 = [[Person alloc]init];
    Person *p2 = [Person new];
    Person *p0 = [Person sharedInstance];
    Person *p = [Person sharedInstance];
    NSLog(@"\n%@==\n%@==\n%@==\n%@",p1,p2,p0,p);

log:
2018-12-04 17:02:32.057731+0800 OCTest[46730:8876108] 
<Person: 0x600003f36d10>==
<Person: 0x600003f36da0>==
<Person: 0x600003f36db0>==
<Person: 0x600003f36db0>

像上面这样显然是不可取的,不同初始化方法初始化的对象的内存不是同一个。

创建单例方法一:重写allocWithZone

OC初始化一个对象分为两步:[[Class alloc]init]
1,给对象分配内存空间(alloc)
2,初始化对象的相关信息 (init)
当调用alloc给对象分配内存时,系统会自动调用allocWithZone为其分配内存。因此重写allocWithZone方法就可以保证,实例化对象的时候,为对象分配的内存永远都是同一个。(ps: alloc与allocWithZone的区别http://justinyan.me/post/1306

#import "Person.h"
@interface Person()<NSCopying,NSMutableCopying>
@end
@implementation Person
+(instancetype)sharedInstance
{
    return [[self alloc]init];
}
-(instancetype)init
{
    if(self = [super init]){
    }
    return self;
}
//重写allocWithZone 保证创建对象分配的内存是同一个
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
    static Person *p = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        p = [super allocWithZone:zone];
    });
    return p;
}

上面的代码就可以保证不管使用的什么初始化方法初始化对象,对象的内存地址只有一个。
既然都写到这里了,那连copy和mutableCopy也一起处理,虽然用到的时候不多,但不重写的情况下,调用copy会引起crash。还是那句话,你并不能保证你的队友不会做什么。

-(instancetype)copyWithZone:(NSZone *)zone
{
    return [Person sharedInstance];
}
-(instancetype)mutableCopyWithZone:(NSZone *)zone
{
    return [Person sharedInstance];
}

创建单例方法二:重写+(void)initialize和+ (instancetype)alloc

当第一次调用一个类时,当这个类是第一次被调用时,会先调用+(void)initialize方法,在调用初始化方法+ (instancetype)alloc;当不是第n>1次时,会直接调用+ (instancetype)alloc方法。因此可以在initialize方法中初始化单例,在alloc做出判断,若对象已经初始化,就不再次初始化,保证对象只初始化一次。

#import "Person.h"
@interface Person()<NSCopying,NSMutableCopying>
@end
@implementation Person

static Person *_p = nil;
//第一次调用该类时调用
+(void)initialize
{
    [self sharedInstance];
}
//初始化对象
+(instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _p = [[self alloc]init];
    });
    return _p;
}
//重写alloc 判断对象是否已经初始化。若已经初始化,就用原来的;若没有就c初始化一个。只有第一次调用该类的时候,才会创建。
+(instancetype)alloc
{
    if (_p) {
        return _p;
    }
    return [super alloc];
}
-(instancetype)copyWithZone:(NSZone *)zone
{
    return [Person sharedInstance];
}
-(instancetype)mutableCopyWithZone:(NSZone *)zone
{
    return [Person sharedInstance];
}

单例优缺点:

优点:
(1)初始化后可供全局调用,方便
(2)只初始化一次,在程序只存在一个对象,节省了系统内存资源。
缺点:
(1)不能被继承
(2)不易被重写(可以用分类)
(3)在程序运行中,不会被销毁,会一直占用部分内存,在闲置时,占用了内存资源。

补充:怎么用宏定义 定义单例类,在类中写上宏定义后,使这个类变为单例类。

在.h中定义宏定义
// .h文件
#define ZYShareSingleton_H(name) \
+(instancetype)shared##name
// .m文件
#define ZYShareSingleton_M(name) \
+(instancetype)shared##name \
{ \
    return [[self alloc]init]; \
} \
+(instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
    static name *instance = nil; \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        instance = [super allocWithZone:zone]; \
    }); \
    return instance; \
} \
-(instancetype)copyWithZone:(NSZone *)zone \
{ \
    return [name shared##name]; \
} \
-(instancetype)mutableCopyWithZone:(NSZone *)zone \
{ \
    return [name shared##name]; \
}

这样,在需要使用单例的类Person中如下定义:
在类的.h中 写上ZYShareSingleton_H(Person);

@interface Person : NSObject
ZYShareSingleton_H(Person);
@end

在类的.m中 写上ZYShareSingleton_M(Person);

@implementation Person
ZYShareSingleton_M(Person);
@end

demo地址:https://github.com/coloredSky/iOS.git

小知识:宏定义中的字符用处

(1)
字符:##
作用:链接字符创
例:

#define KName(n) x ## n
=>使用
KName(8)
=>输出
x8

(2)
字符:\
作用:续行符,当定义的宏不能用一行表达完整时,用 \ 表示下一行继续此宏的定义。
例:

#define K_Man(a,b) \
a>=b?a:b
=>使用
K_Man(2,3)
=>输出
3

(3)
字符:#
作用:字符串化。给参数加双引号。
例:

#define K_Char(n) #n
=>使用
K_Char(888)
=>输出
"888"

(4)
字符:VA_ARGS
作用:用来接受不定数量的参数。可以是一个、两个、n个参数
例:

#define K_Log(...) NSLog(__VA_ARGS__)
=>使用
K_Log(@"%@%@",@"111",@"222");
=>输出
111222

(5) ## 的特殊用法
"##"放在逗号","和参数之间,如果参数留空的话,那么"##"前面的","就会被删掉,从而防止编译错误。
例:

#define K_Log(parameter1, ...) NSLog(parameter1, __VA_ARGS__)
K_Log(@"11111");//会报错,因为至少需要传递两个参数,而只传了一个
#define K_Log(parameter1, ...) NSLog(parameter1, ##__VA_ARGS__)
K_Log(@"11111");//不会报错,虽然只传了一个参数,但##将前面的","去除了
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280

推荐阅读更多精彩内容