音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

使用设计模式中的Singleton单例模式来开发iOS应用程序
日期:2016-03-16 18:12:26   来源:脚本之家

单例设计模式确切的说就是一个类只有一个实例,有一个全局的接口来访问这个实例。当第一次载入的时候,它通常使用延时加载的方法创建单一实例。

提示:苹果大量的使用了这种方法。例子:[NSUserDefaults standerUserDefaults], [UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager] 都返回一个单一对象。
你可能想知道你为什么要关心一个类有多个的实例。代码和内存都很便宜,不是吗?

在一些情况下,一个类只有一个实例是有意义的。例如,这里没有必要有多个登录实例,除非你一次想写入多个日志文件。或者,一个全局的配置类文件:它可以很容易的很安全的执行一个公共资源,这样的一个配置文件,要比同时修改多个配置类文件好很多。

如何使用单例模式

请看下面的图片

2016316181231772.png (233×152)

上面的图片显示的是一个登录类,它有一个属性(这个单一实例),有两个方法:sharedInstance 和 init。

首先一个客户端(client)发送 sharedInstance 信息,但是属性 instance 还没有初始化,所以你要先给这个类创建一个实例。

然后你调用 sharedInstance,instance 会马上返回初始化的值。这个逻辑最终只会返回一个实例。

你需要执行这个模式来创建单例类来管理所有的专辑数据。

你需要注意在项目里有一个叫 API 文件夹,给你的 APP 提供服务的所有类都需要放在这里。在这个文件夹里用 iOS\Cocoa Touch\Object-C class 创建一个新类。类的名字叫 LibraryAPI,子类选择 NSObject。

打开 LibraryAPI.h 文件用下面的代码替换里面的内容:

复制代码 代码如下:

@interface LibraryAPI: NSObject
+ (LibraryAPI*)sharedInstance;
@end

现在打开 LibraryAPI.m 文件,在 @implentation 后面添加如下方法:
复制代码 代码如下:

+ (LibraryAPI*)sharedInstance
{
    // 1
    static LibraryAPI *_sharedInstance = nil;

    // 2
    static dispatch_once_t oncePredicate;

    // 3
    dispatch_once(&nocePredicate, ^{
        _sharedInstance = [[LibraryAPI alloc] init];
    });
    return _sharedInstance;
}


在这个短方法中做了这些事情:

在这个类中,声明一个静态变量来保存这个实例,保证它是一个全局可用的变量。
声明一个静态这是 dispatch_one_t,确保这些初始化代码只能被执行一次。
使用 Grand Central Dispatch(GCD)执行一个 block 来初始化 LibraryAPI 实例。这是单例设计模式的关键所在:一个类只能被实例化一次。
接下来执行 sharedInstance,在 dispatch_once block 里的代码是不会被执行的(当它已经被执行过一次后),它会返回之前创建的 LibraryAPI 实例。

提示:想了解更多关于 GCD 和使用它,请点击这里的教程 Multithreading and Grand Central Dispatch,如何使用 Blocks 在这里。
你现在有一个单例对象来管理专辑了。下一步就是创建一个类用来保存你的专辑数据了。

用 iOS\Cocoa Touch\Object-C class 在 API 文件夹下创建一个新的类,名字叫 PersistencyManager,子类选择 NSObject。

打开 PersistencyManager.h,在顶部引入面文件:

#import "Album.h"
然后在 @interface 后面加入下面代码:

复制代码 代码如下:

- (NSArray *)getAlbums;
- (void)addAlbums:(Album*)album atIndex:(int)index;
- (void)deleteAlbumAtIndex:(int)index;

上面的三个方法都需要跟专辑的数据相结合。

打开 PersistencyManager.m,在 @implementation 上面添加如下代码:

复制代码 代码如下:

@interface PersistencyManager () {
    NSMutableArray *albums;
}

上面的代码是给类添加了一个扩展,这是另一种给类添加私有方法和私有属性的方法,类外面的成员是看不到这些的。这里,你声明了一个 NSMutableArray 来保存专辑的数据。这是一个可变数组,你可以很容易的添加和删除专辑。

现在在 @implementation 下面添加实现代码:

复制代码 代码如下:

- (id)init {
    self = [super init];
    if (self) {
        albums = [NSMutableArray arrayWithArray:@[[[Album alloc] initWithTitle:@"Best of Bowie" artist:@"David Bowie" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png" year:@"1992"],
        [[Album alloc] initWithTitle:@"It's My Life" artist:@"No Doubt" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png" year:@"2003"],
                [[Album alloc] initWithTitle:@"Nothing Like The Sun" artist:@"Sting" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png" year:@"1999"],
            [[Album alloc] initWithTitle:@"Staring at the Sun" artist:@"U2" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png" year:@"2000"],
                [[Album alloc] initWithTitle:@"American Pie" artist:@"Madonna" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:@"2000"]]];
    }
    return self;
}

在 init 里你在数组中加入了 5 张专辑。如果上面的专辑你不喜欢,你可以随意替换成你喜欢的。:]

现存在 PersistencyManager.m 添加下面三个方法:

复制代码 代码如下:

- (NSArray*)getAlbums
{
        return albums;
}

- (void)addAlbum:(Album*)album atIndex:(int)index
{
        if (albums.count >= index)
            [albums insertObject:album atIndex:index];
        else
        [albums addObject:album];
}

- (void)deleteAlbumAtIndex:(int)index
{
        [albums removeObjectAtIndex:index];
}


这些方法是获取,添加,删除专辑。

Build 你的项目,确保所有的代码都能正确编译。


单例模式的使用场合

类只能有一个实例,并且必须从一个为人数值的访问点对其访问。
这个唯一的实例只能通过子类化进行拓展,并且拓展的对象不会破坏客户端代码。

在Objective-C中方法都是公有的,而且OC的语言本身是动态类型的,因此所有类都可以相互发送对方的消息。,并且Cocoa框架使用计数的内存管理方式来维护对象的内存中的生存期。
下面让我们看一下OC当中的单例模式的写法,首先单例模式在ARC\MRC环境下的写法有所不同,需要编写2套不同的代码

可以用宏判断是否为ARC环境#if _has_feature(objc_arc)

复制代码 代码如下:

#else
//MRC
#endif

单例模式- ARC -方法一

ARC中单例模式的实现
在 .m中保留一个全局的static的实例

复制代码 代码如下:

 static id _instance;
 //重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)
 + (instancetype)allocWithZone:(struct _NSZone *)zone
{
    @synchronized(self) {
        if (_instance == nil) {
            _instance = [super allocWithZone:zone];
        }
    }
    return _instance;
}

提供1个类方法让外界访问唯一的实例
复制代码 代码如下:

    + (instancetype)sharedInstanceTool{
    @synchronized(self){
        if(_instance == nil){
            _instance = [[self alloc] init];
        }
    }
    return _instance;
}

实现copyWithZone:方法

复制代码 代码如下:

  -(id)copyWithZone:(struct _NSZone *)zone{
  return _instance;
  }

我们在sharedInstanceTool,首先检查类的唯一实例是否已经创建,如果就会创建实例并将其返回。而之所以调用super而不是self,是因为已经在self中重载了基本的对象分配的方法,需要借用父类的功能来帮助处理底层内存的分配。
在allocWithZone:(struct _NSZone*)zone方法中,只是返回从sharedInstanceTool方法返回的类实例。而同样的在Cocoa框架中调用allocWithZone:(struct _NSZone*)zone会分配内存,引用计数会设置为1,然后返回实例。同样的重写(id)copyWithZone:(struct _NSZone *)zone方法,也是为了保证不会返回实例的副本,而是返回self.返回同一个实例。


方法二:

复制代码 代码如下:

+(instancetype)sharedInstance {
    static WMSingleton *singleton = nil;
    if (! singleton) {
        singleton = [[self alloc] initPrivate];
    }
    return singleton;
}

- (instancetype)init {
    @throw [NSException exceptionWithName:@"这个是个单例"
                                   reason:@"应该这样调用 [WMSingleton sharedInstance]"
                                 userInfo:nil];
    return nil;
}
//实现自己真正的私有初始化方法
- (instancetype)initPrivate {
    self  = [super init];
    return self;
}


上面这段代码中将singleton指针声明为静态变量。当某个定义了静态变量的方法返回时,程序不会释放相应的变量。####singleton变量的初始值是nil,当程序第一次执行sharedInstance方法时会创建一个对象,并将新创建的对象的地址赋值给singleton变量。当徐成再次执行sharedInstance方法时,无论多少次singleton变量仍然会指向最初那个创建的对象。因为指向对象的singleton变量是强引用的,并且程序永远不会释放该变量,所以singleton变量指向的对象也不会释放。
线程安全。
上面的实例中我们通过@synchronized来添加了一个互斥锁,以此来保证线程安全。而现在我们开始尝试用线程的方式来实现一个加单的单例。

复制代码 代码如下:

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

+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}

- (id)copyWithZone:(NSZone *)zone
{
    return _instance;
}


从上面的代码我们可以看到,实现的思路基本上也是一致的我们在sharedInstanceTool,首先检查类的唯一实例是否已经创建,如果就会创建实例并将其返回。而略有不同的地方就是我们这次通过dispatch_once_t来保证线程的安全性。至于dispatch_once_t的用法这里就一一赘述了,线程的相关教程都会有其相关的描述。
到了这里一个简单的单例模式基本实现完成了,那么我们可以尝试着把它封装到一个宏里,然后方便其以后的调用
创建一个WMSingleton.h
复制代码 代码如下:

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

// .m文件
#define WMSingletonM(name) \
static id _instance; \
 \
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instance = [super allocWithZone:zone]; \
    }); \
    return _instance; \
} \
 \
+ (instancetype)shared##name \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instance = [[self alloc] init]; \
    }); \
    return _instance; \
} \
 \
- (id)copyWithZone:(NSZone *)zone \
{ \
    return _instance; \
}


使用方法
复制代码 代码如下:

//.h类
//引入这个宏文件
#import "WMSingleton.h"
@interface WMObject : NSObject
WMSingletonH(object)
@end
//.m类

@implementation WMObject
WMSingletonM(Car)
@end

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    Windows系统Linux系统苹果MACAndroidiOS系统鸿蒙系统