iOS 单例模式

单例模式

2015-11-06  本文已影响209人  952625a28d0d
Paste_Image.png
[[UIApplication sharedApplication] statusBarStyle];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notififationAction) name:@"noti" object:nil];
    [[NSUserDefaults standardUserDefaults] setObject:@"user" forKey:@"user"];
    [NSFileManager defaultManager];```

观察系统的单例类之后,我们都会发现单例的类都会通过一个方法来获取这个类实例,然后通过这个实例来做一些事情。单例一般都是用来管理某种资源,某种对象,而这个对象拥有某些属性供全局使用。
大部分情况,我们使用单例是为了共享信息。
缺点:因为他共享了信息,破坏了最少支持原则
破坏了封装性。
但是解决实际问题的时候也是可以忽略的。

- 我们来写一个用户信息管理中心单例

import <Foundation/Foundation.h>

@interface UserInfoManagerCenter : NSObject

@property(nonatomic, copy) NSString * name;
@property(nonatomic, strong) NSNumber * age;

@end```

h:

#import "UserInfoManagerCenter.h"

@implementation UserInfoManagerCenter

#pragma mark - 第三种创建单例的方法,不好,即写在这个类里面,因为此类是任何一个类在调用类方法之前必须调用的方法
+ (void)initialize{
    static UserInfoManagerCenter *center = nil;
    if (self == [UserInfoManagerCenter class]) {
        center = [[UserInfoManagerCenter alloc] init];
    }
    
}

#pragma mark - 一般解决方案,不好,如果AppDelegate和ViewController同时访问这个单例,如果同时进入方法进行初始化,那么就会出现问题
+ (instancetype)managerCenter{
    
    // 用静态变量持有对象
    static UserInfoManagerCenter *center = nil;
    if (center == nil) {
        center = [[UserInfoManagerCenter alloc] init];
    }
    return center;
}

#pragma mark - 好的解决方案,用dispatch_once来解决他们的竞争问题
+ (instancetype)managerCenter{
    static UserInfoManagerCenter *center = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        center = [[UserInfoManagerCenter alloc] init];
    });
    return center;
}

@end```

- 使用单例
AppDelegate中赋值

viewController中取出

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    [[UIApplication sharedApplication] statusBarStyle];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notififationAction) name:@"noti" object:nil];
    [[NSUserDefaults standardUserDefaults] setObject:@"user" forKey:@"user"];
    [NSFileManager defaultManager];
    
    UserInfoManagerCenter *center = [UserInfoManagerCenter managerCenter];
    NSLog(@"%@%@",center.name,center.age);
    
}```


![Paste_Image.png](http:https://img.haomeiwen.com/i189984/61a92c34d4be032a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

以上是一个单例的典型用法,但是并不是完美单例,因为我们还能通过alloc init方法再次创建对象,严格来说是不行的。

- 那么如何实现一个严格的单例模式?

我们可以写一个方法,以检测单例方法实现只有UserInfoManager类在调用,如果是别的类在调用会直接导致崩溃,这个怎么实现呢?

- 如何防止子类继承?

pragma mark - 好的解决方案,用dispatch_once来解决他们的竞争问题

Paste_Image.png

上图我们用两种方法创建了单例类的实例对象,发现打印出来的地址并不一样,这就造成了单例对象不唯一的问题

在一个系统中,单例作为一个管理中心对象,他的开销是非常大的,所以保持唯一性非常重要,我已我们不能让单例通过alloc init方法初始化,换句话说我们要让这种初始化方法失效。

Paste_Image.png

在单例里面

Paste_Image.png
#pragma mark - 重写单例类的初始化方法
- (instancetype)init{
    // 用字符串接收静态变量
    NSString *string = (NSString *)center;
    // 判断是否是字符串类并且等于创建单例方法中的字符串
    if ([string isKindOfClass:[NSString class]] && [string isEqualToString:@"UserInfoManagerCenter"]) {
        self = [super init];
        if (self) {
            
        }
        return self;
    }else{
        // 如果不,则初始化返回为nil
        return nil;
    }
}
@end```

- 运行结果

![Paste_Image.png](http:https://img.haomeiwen.com/i189984/f9501aa9dc52cfd6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

发现用alloc init创建的对象已经为nil
当然我们也需要在init方法中实现子类的alloc init滞空

pragma mark - 重写单例类的初始化方法

这里给大家推荐一个第三方: FastCoding
github地址为:https://github.com/nicklockwood/FastCoding
这个第三方的强大之处在于能够不遵守系统的NSCoding协议对模型、数组、等等数据进行存储!下面我们用单例结合这个第三方对数据存储封装一下吧!
首先,引入引入第三方运行之后发现一个警告!

Paste_Image.png Paste_Image.png

警告的意思是我们的项目使用了ARC,这个第三方在ARC下运行缓慢,我们在这个文件后面添加-fno-objc-arc即可

Paste_Image.png

再次运行发现警告消失

开始编写代码:
首先写一个单例用来存储数据:

#import <Foundation/Foundation.h>

@interface StoreValue : NSObject

+ (StoreValue *)shareInstance;  // 单例初始化

- (void)storeValue:(id)object withKey:(NSString *)key;  // 存储值
- (id)valueWithKey:(NSString *)key; // 取出

@end```

m文件

import "StoreValue.h"

import "FastCoder.h" // 引入头文件

@implementation StoreValue

}

@end```

Paste_Image.png

已经有了值
下面我们来写一个复杂的存储,看看能不能实现,首先我们来写两个模型

Paste_Image.png
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
//    NSArray *array = @[@"贾元发"];
//    [[StoreValue shareInstance] storeValue:array withKey:@"jia"];
//    NSLog(@"%@",[[StoreValue shareInstance] valueWithKey:@"jia"]);
    Student *student = [[Student alloc] init];
    student.name = @"jiayuanfa";
    NSArray *array = @[[Teacher new],[Teacher new],[Teacher new]];
    student.teachersArray = array;
    [[StoreValue shareInstance] storeValue:student withKey:@"stu"];
    Student *student1 = [[StoreValue shareInstance] valueWithKey:@"stu"];
    NSLog(@"%@",student1.name);
    NSLog(@"%@",student1.teachersArray);

}```

运行结果:

![Paste_Image.png](http:https://img.haomeiwen.com/i189984/bd2dbc0d84fbd362.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
发现复杂的数据我们也能存储

- 用类目对单例存储进行进一步上层的封装以便我们用对象直接调用存储的方法

![Paste_Image.png](http:https://img.haomeiwen.com/i189984/97c7257f1ce76cd2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

import <Foundation/Foundation.h>

@interface NSObject (StoreValue)

// 添加类目方法

@end```

m

#import "NSObject+StoreValue.h"
#import "StoreValue.h"

@implementation NSObject (StoreValue)

- (void)storeValueWithKey:(NSString *)key{
    [[StoreValue shareInstance] storeValue:self withKey:key];
}

+ (id)valueWithKey:(NSString *)key{
    return [[StoreValue shareInstance] valueWithKey:key];
}

@end```

import "ViewController.h"

import "StoreValue.h"

import "Teacher.h"

import "Student.h"

import "NSObject+StoreValue.h"

@interface ViewController ()

@end

@implementation ViewController

// NSArray *array = @[@"贾元发"];
// [[StoreValue shareInstance] storeValue:array withKey:@"jia"];
// NSLog(@"%@",[[StoreValue shareInstance] valueWithKey:@"jia"]);
Student *student = [[Student alloc] init];
student.name = @"jiayuanfa";
NSArray *array = @[[Teacher new],[Teacher new],[Teacher new]];
student.teachersArray = array;
// [[StoreValue shareInstance] storeValue:student withKey:@"stu"];
// 模型调用直接存储
[student storeValueWithKey:@"stu"];
Student *student1 = [Student valueWithKey:@"stu"];
NSLog(@"%@",student1.name);
NSLog(@"%@",student1.teachersArray);

}```

结果:

Paste_Image.png

以上我们对单例存储用类目进行了进一步的封装。

需要掌握的技术:
1:单例核心原理
2:严格单例思想
3:用单例优化存储模式

上一篇下一篇

猜你喜欢

热点阅读