猫猫学iOS(五十二)多线程网络之GCD下单例设计模式

猫猫分享,必须精品

原创文章,欢迎转载。转载请注明:翟乃玉的博客
地址:http://blog.csdn.net/u013357243?viewmode=contents

单例模式

1:单例模式的作用

可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问
从而方便地控制了实例个数,并节约系统资源

单例模式的使用场合
在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)

简单来说,就是我弄了一个工具类,他就有一份,比如我设计了一个音乐播放器NYPlayer,这个播放器类我就想他有一个,这就是单例,我用的时候我只需要让播放器干活,如果多个的话我不知道指定那个,或者有重复会出bug。
百度百科是这样说的:单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
简单来说,就是让一个类的事物里面只有一个对象。

2:ARC中,单例模式的实现

过程:
在.m中保留一个全局的static的实例
这里要用static,记住他,稍后我回写一个关于static关键词的博客。

static id _instance;

重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)
创建类的时候调用[类名 alloc]方法的时候,内部会自动调用allcWithZone方法,加载一个空间,所以我们只要重写这个方法,当我们定义的全局变量instance有值得时候,就会直接返回,如果没有,就会调用父类方法[super allocWithZone:zone]并赋值给instance,但是这时候有一个危险,那就是如果这里有两个线程都进来了,那样就会分配两次,所以我们要给本段程序加锁。

+ (id)allocWithZone:(struct _NSZone *)zone {
    if (_instance == nil) { // 防止频繁加锁
        @synchronized(self) {
            if (_instance == nil) { // 防止创建多次
          _instance = [super allocWithZone:zone];
            }
        }
    }
    return _instance;
}

提供一个类方法让外界访问唯一的实例。
跟上面的allocWithzone一样,不过有所不同的是这里调用的时【[self alloc] init】方法。

+ (instancetype)sharedMusicTool {
    if (_instance == nil) { // 防止频繁加锁
            @synchronized(self) {
            if (_instance == nil) { // 防止创建多次
               _instance = [[self alloc] init];
            }
        }
    }
    return _instance;
}

实现copyWithZone:方法
- (id)copyWithZone:(struct _NSZone *)zone {
    return _instance;
}

3:MRC中的单例模式

过程:
非ARC中(MRC),单例模式的实现(比ARC多了几个步骤)
实现内存管理方法。

- (id)retain { return self; }//父类里面是计数器+1,重写将不会+1
- (NSUInteger)retainCount { return 1; }//父类里面返回当前对象的计数个数
- (oneway void)release {}//父类里面是计数器-1,重写将不会-1
- (id)autorelease { return self; }//父类里面内存自动管理释放的,跟内存池有关

GCD下完美单例模式(ARC&MRC通吃)

判断ARC的宏

这里首先要知道一个宏了 __has_feature(objc_arc)

单例模式在ARC\MRC环境下的写法有所不同,需要编写2套不同的代码
可以用宏判断是否为ARC环境。

#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif

传值的宏

就一句

#define NYSingletonH(name) + (instancetype)shared##name;

用的时候传进name,他就会自动替换了。

用GDC运行一次代码解决

怎么解决呢 ?其实就是把加锁换成GCD的一次性代码

代码如下:

+(id)allocWithZone:(struct _NSZone *)zone
    {
        if (_instance == nil) {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                if (_instance == nil) {
                    _instance = [super allocWithZone:zone];
                }
            });
        }
        return _instance;
    }

快捷方式,xcode中只要敲dispatch_once就自动有啦,填填写写。

终极方式,以后单例不用敲代码了,直接掉就行了。

第一步,简历一个.h文件,例如
技术分享

第二步,文件中写入以下代码:

// .h文件
#define NYSingletonH(name) + (instancetype)shared##name;

// .m文件
#define NYSingletonM

#if __has_feature(objc_arc)
#define NYSingletonM(name)    static id _instance;    +(id)allocWithZone:(struct _NSZone *)zone    {        if (_instance == nil) {            static dispatch_once_t onceToken;            dispatch_once(&onceToken, ^{                if (_instance == nil) {                    _instance = [super allocWithZone:zone];                }            });        }        return _instance;    }    +(instancetype)shared##name    {        if (_instance == nil) {            static dispatch_once_t onceToken;            dispatch_once(&onceToken, ^{                if (_instance == nil) {                    _instance = [[self alloc]init];                }            });        }        return _instance;    }    -(id)copyWithZone:(NSZone *)zone    {        return _instance;\\
    }

#else
#define NYSingletonM(name)    static id _instance;    +(id)allocWithZone:(struct _NSZone *)zone    {        if (_instance == nil) {            static dispatch_once_t onceToken;            dispatch_once(&onceToken, ^{                if (_instance == nil) {                    _instance = [super allocWithZone:zone];                }            });        }        return _instance;    }    +(instancetype)shared##name    {        if (_instance == nil) {            static dispatch_once_t onceToken;            dispatch_once(&onceToken, ^{                if (_instance == nil) {                    _instance = [[self alloc]init];                }            });        }        return _instance;    }    -(id)copyWithZone:(NSZone *)zone    {        return _instance;    }    -(id)retain    {        return self;    }    -(NSUInteger)retainCount{return 1;}    -(oneway void)release{}    -(id)autorelease{return self;}


#endif

\符号是让后面的东东包含到上一行去,因为define只能定义一行。

第三步:在pch文件中写入调用代码:

#import "NYSingleton.h"

技术分享

第四步:在需要用到的类中调用:

在点h文件中调用这个
技术分享

在点m文件中调用这个
技术分享

最后调用,还是单例:
技术分享

补充,自定义宏代替NSLog

注意:开发过程中,会使用NSLog来打印信息用于调试,但releae的软件却不能包含NSLog,可能会有被打回的风险,但是要是全部注释掉NSLog那太痛苦了,也不利于以后的调试。

下面,我们可以采用自定义宏来取代NSLog,只在我们想要的时候输出


/*
 XCode LLVM XXX - Preprocessing中Debug会添加 DEBUG=1 标志
 */
#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr,"%s:%d\t%s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSLog(FORMAT, ...) nil
#endif

把以上代码粘贴到ProjectName-Prefix.pch文件中。

在调试的时候,会输出(格式:文件名:行号)日志。

在Release正式版本的时候,会关闭日志输出。

因为XCode LLVM XXX - Preprocessing中Debug会添加 DEBUG=1 标志。当然我们也能把DEBUG直接换成数字1来设置。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。