Objective-c:设计模式

原创 2015年11月17日 23:53:14

单例模式

单例简介

  • 单例是生命周期与程序生命周期相同,仅能生成一次、且不能被销毁的唯一实例;

  • 需要确保应用中的一个特定类有且仅有一个实例(对象);

  • 单例可在程序任何位置被访问,且一直存在;

  • 单例实例获取方法命名规则:一般以standard…、shared…、default….开头;

  • 单例必须至少满足以下条件:

    • 只初始化一次;

    • 全局存在;

    • 唯一,对象不可改;

    • 方便获取,任何位置可访问

系统单例

  • UIApplication:应用程序单例

  • NSNotificationCenter:通知中心单例

  • NSUserDefaults:用户默认单例

  • NSFileManager:文件管理单例

单例优点

  • 共享信息;

  • 实例控制:Singleton会阻止其他对象实例化其自己的Singleton对象的副本,从而确保所有对象都访问唯一实例;

  • 灵活性:因为类控制了实例化过程,所以类可以更加灵活地修改实例化过程;

自定义单例创建常用方法

首先创建一个继承于NSObject的类Singleton,其次在Singleton.m文件中实现如下代码;

  • 方法1:一般创建
#import "Singleton.h"

// 静态变量保证单例对象始终存在
static Singleton *singleton = nil;

@implementation Singleton

+ (instancetype)defaultSingleton {

    // 仅在单例对象为空时初始化一次,之后直接返回唯一的单例对象
    if (singleton == nil) {
        singleton = [[Singleton alloc] init];
    }
    return singleton;
}

@end
  • 方法2:GCD创建(推荐使用)
#import "Singleton.h"

static Singleton *singleton = nil;

@implementation Singleton

+ (instancetype)defaultSingleton {

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        singleton = [[Singleton alloc] init];
    });
    return singleton;

}

@end

自定义单例创建完整方法

  • 一般创建单例方法可能会出现一些问题,我们通过不同的途径得到不同的对象,显然是不行的。我们必须要确保对象的唯一性,所以我们就需要封锁用户通过alloc/init/copy以及release/retain等方法来构造或操作对象这条道路。

  • 我们知道,创建对象的步骤分为申请内存(alloc)、初始化(init)这两个步骤,我们要确保对象的唯一性,因此在第一步这个阶段我们就要拦截它。当我们调用alloc方法时,oc内部会调用allocWithZone这个方法来申请内存,我们覆写这个方法,然后在这个方法中调用defaultSingleton方法返回单例对象,这样就可以达到我们的目的。拷贝对象也是同样的原理,覆写copyWithZone方法,注意需遵守<NSCopying>协议,然后在这个方法中调用defaultSingleton方法返回单例对象,其他示例直接参考如下代码:

static Singleton *singleton = nil;

+ (instancetype)defaultSingleton {

    if (!singleton) {
        singleton = [[Singleton alloc] init];
    }
    return singleton;
}

// alloc
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    @synchronized(self) {
        if (!singleton) {

            singleton = [super allocWithZone:zone];

            return singleton;
        }
        return nil;
    }
}

// copy
- (id)copyWithZone:(NSZone *)zone {
    return self;
}

// retain
- (instancetype)retain {
    return self;
}

// release
- (oneway void)release {

}

// autorelease
- (instancetype)autorelease {
    return self;
}

// retainCount
- (NSUInteger)retainCount {
    return NSUIntegerMax;
}

单例使用

首先在main.m文件中,引入“Singleton.h”,其次创建两个单例实例,并且打印对象信息;

这里写图片描述

我们可以看到,两个实例对象的地址是一致的,由此可见,单例的唯一性。

可利用单例的唯一性进行传值,但必须满足一个前提条件就是,在传值时,必须保证单例存在并且单例属性确实有值。

现在在Singleton.h文件中声明属性name,其次创建一个Person类,为其设置属性name,并且重写init方法,通过单例对name直接复制,如下所示:

#import "Person.h"
#import "Singleton.h"

@implementation Person

- (instancetype)init {
    self = [super init];
    if (self) {
        // 获取单例属性值赋给name
        self.name = [Singleton defaultSingleton].name;
    }
    return self;
}

@end

切换到main.m文件中,创建单例实例,并且给name属性复制,其次实例化Person对象,并打印person.name,如下所示:

#import <Foundation/Foundation.h>

#import "Singleton.h"

#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {

#pragma mark - 单例模式
        Singleton *singleton = [Singleton defaultSingleton];
        singleton.name = @"Admin";

        Person *person = [[Person alloc] init];

        NSLog(@"Person's name is '%@'.", person.name); //  Person's name is 'Admin'.
    }
    return 0;
}

从上述代码中可以看出,可通过单例进行传值操作。

KVC 键值编码

概述

  • KVC是KeyValueCoding的简称,它是一种可以直接通过字符串的名字(key)来访问类属性(实例变量)的机制。而不是通过调用Setter、Getter方法访问。通常我们使用valueForKey来替代getter() 方法,setValue:forKey来代替setter()方法。

  • 当使用KVO、Core Data、CocoaBindings、AppleScript(Mac支持)时,KVC是关键技术。

常用方法

  • 取值方法
// 1、
valueForKey:,传入NSString属性的名字。

// 2、
valueForKeyPath:,传入NSString属性的路径,xx.xx形式。
  • 设值方法
// 1、
- (void)setValue:(nullable id)value forKey:(NSString *)key;

// 2、
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

// 3、- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

// 4、
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

KVC 读取方式

KVC使用起来比较简单,但是它如何查找一个属性进行读取呢?具体查找规则(假设现在要利用KVC对name进行读取):

  • 如果是动态设置属性,则优先考虑调用setName方法,如果没有该方法则优先考虑搜索成员变量_name,如果仍然不存在则搜索成员变量name,如果最后仍然没搜索到则会调用这个类的setValue:forUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确设置);

  • 如果是动态读取属性,则优先考虑调用name方法(属性name的getter方法),如果没有搜索到则会优先搜索成员变量_name,如果仍然不存在则搜索成员变量name,如果最后仍然没搜索到则会调用这个类的valueforUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确读取);

案例

1、声明 KVCClass 类,并在其.h文件中声明几个全局变量,无需定义成属性及生成存(setter)取(getter)方法,代码如下:

#import <Foundation/Foundation.h>

@interface KVCClass : NSObject {

    NSString *_name;        /**< 姓名 */
    NSString *_nationality; /**< 国籍 */
    NSInteger _age;         /**< 年龄 */
}

@end

2、在 “KVCClass.m”文件中,重写description方法与dealloc方法,代码如下:

#import "KVCClass.h"

@implementation KVCClass

- (void)dealloc {
    [_name release]; _name = nil;
    [_nationality release]; _nationality = nil;

    [super dealloc];
}

- (NSString *)description {
    return [NSString stringWithFormat:@"<%p name:%@, age:%ld, address:%@>", self, _name, _age, _nationality];
}

@end

3、在main.m文件中,导入KVCClass.h,实例化对象,并通过KVC为其设值,代码如下:

#import <Foundation/Foundation.h>
#import "KVCClass.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {

#pragma mark - KVC 

        // 1、逐一赋值
        KVCClass *kvcObject1 = [[KVCClass alloc] init];

        [kvcObject1 setValue:@"Charles" forKey:@"name"];
        [kvcObject1 setValue:@"China" forKey:@"nationality"];
        [kvcObject1 setValue:@22 forKey:@"age"];

        NSLog(@"%@", kvcObject1);

        // 取值
        NSLog(@"Name is '%@'.", [kvcObject1 valueForKey:@"name"]);


        // 2、统一赋值
        KVCClass *kvcObject2 = [[KVCClass alloc] init];

        NSDictionary *dic = @{@"name":@"William", @"nationality":@"USA", @"age":@30};

        [kvcObject2 setValuesForKeysWithDictionary:dic];


        NSLog(@"%@", kvcObject2);

        // 取值
        NSLog(@"Name is '%@'.", [kvcObject2 valueForKey:@"name"]);

    return 0;
}

控制台输出情况如下:

这里写图片描述

Tips

1、key的值必须正确,如果拼写错误,会出现异常;

2、当key的值是没有定义的,valueForUndefinedKey:这个方法会被调用,如果你自己写了这个方法,key的值出错就会调用到这里来;

3、因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去;

4、NSArray/NSSet等都支持KVC;

KVO 键值监听

概述

  • KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。在ObjC中要实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是NSObject已经实现了该协议,因此几乎所有的ObjC对象都可以使用KVO。

  • 在面对对象编程时,我们很多情况下直接处理某些事件会很麻烦。如常见的程序运行中电话呼入、用户突然点击home键等等,一般我们使用观察者(代理)来处理类似事件。

  • 在使用这些模式的时候,我们需要告诉这些角色,谁来响应这些事件,这就需要使用类的引用。

  • 类的引用本质上就是如何让另外一个角色,“知道”另外一个角色,对应到代码中,就是如何让某一个类的对象,顺利的把信息传递给另外一个对象。

  • 处理类的引用的时候,应注意以下几点:

    • 每个类是否能正常接收到信息(响应动作)

    • 信息传递时接收者是否存在

    • 每个类是否能正常释放(不会引起循环引用)

键值观察

  • 键值观察是基于键值编码的一种技术;

  • 利用键值观察可以注册成为一个对象的观察者,在该对象的某个属性变化时收到通知;

  • 被观察对象需要编写复合KVC标准的存取方法;

常用方法

系统框架已经支持KVO,所以程序员在使用的时候非常简单。

1、注册指定Key路径的监听器:addObserver: forKeyPath: options: context:

2、实现回调方法: observeValueForKeyPath: ofObject: change: context:

3、删除指定Key路径的监听器:removeObserver: forKeyPath、removeObserver: forKeyPath: context:

案例

此案例将KVC与KVO结合使用

1、声明 KVOClass 类,并在其.h文件中声明全局变量_weather

#import <Foundation/Foundation.h>

@interface KVOClass : NSObject {

    NSString *_weather; /**< 天气 */
}

@end

2、在“person.m”文件中实现如下代码:

#import "KVOClass.h"

@implementation KVOClass

- (void)dealloc {

    // 移除监听
    [self removeObserver:self forKeyPath:@"weather"];

    [_weather release]; _weather = nil;

    [super dealloc];
}

- (instancetype)init {
    self = [super init];
    if (self) {
        // 注册监听
        [self addObserver:self forKeyPath:@"weather" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    }
    return self;
}

// 实现监听回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {

    NSLog(@"天气发生变化,此时天气情况:%@", change[@"new"]);
}

@end

3、在“main.m”文件中导入KVOClass.h文件,实现如下代码,观察控制台:

#import <Foundation/Foundation.h>
#import "KVOClass.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {

#pragma mark - KVO

        KVOClass *kvoObject = [[KVOClass alloc] init];

        [kvoObject setValue:@"晴" forKey:@"weather"];

        [kvoObject release];


    }
    return 0;
}

控制台输出:

2015-11-17 23:02:00.860 DesignPatterns[1392:132353] 天气发生变化,此时天气情况:晴

Tips

使用KVC可以大大地减少我们的代码量,当遇到property的时候,可以多想想是否可以KVC来帮助我,是否可以用KVC来重构代码, 当需要加入observer模式时,可以考虑下KVO, 在高性能的observer里面,KVO会给我们很好的帮助。

数据持久化

  • iOS应用程序使用沙盒机制作为文件存储方式;

  • 应用程序有一个独立的沙盒文件夹,程序仅能访问自己沙盒路径下的文件,任何情况下都无法访问其它程序的沙盒;

  • 那我们可以通过什么方法将数据存入沙盒,同时也可以取出?可同 NSUserdefaults 或 属性列表实现。

  • 数据持久化的方法还有很多,如本地数据库(CoreData、sqlite、归档)、网络数据库(服务器)等等,会在后面的博客中介绍。

NSUserdefaults

常用方法

1、存值

// 1、获取系统单例
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

// 2、设值
[defaults setObject:@"William" forKey:@"name"];

// 3、同步数据
[defaults synchronize];

2、取值

// 1、获取系统单例
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

// 2、取值
NSString *name = [defaults objectForKey:@"name"];

// 3、打印信息
NSLog(@"Name is '%@'.", name);

3、移除值

// 1、获取系统单例
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

// 2、删除值
[defaults removeObjectForKey:@"name"];

// 3、同步数据
[defaults synchronize];

Tips

1、从上述示例中可以看出,无论是什么操作,都需获取userdefaults单例,如果是存值或删除值,一定记得调用synchronize方法,同步数据。

2、通过userdefaults同样可以进行值的传递,因为userdefaults本质也是单例。

读写文件

我们可以自己通过代码,根据一条路径自动在桌面上创建一个文件,来存取我们的数据,注意:这个数据并非存在沙河中。

获取桌面文件路径方式:直接将桌面文件拖入Xcode工程即可,Xcode会自动获取文件路径。

NSDictionary *dict = @{@"account":@"Admin", @"password":@"123456"};

// 1、写入文件
// 将 dict 存入文件,/infomation.text 为自己给存储对象文件的命名
[dict writeToFile:@"/Users/LiHongyao/Desktop/Datas/infomation.text" atomically:YES];

// 2、读取文件
NSDictionary *infomation = [NSDictionary dictionaryWithContentsOfFile:@"/Users/LiHongyao/Desktop/Datas/infomation.text"];

NSLog(@"%@", infomation);

输出情况:

2015-11-17 23:49:39.302 DesignPatterns[1562:170884] {
    account = Admin;
    password = 123456;
}
Program ended with exit code: 0
版权声明:本文为博主原创文章,转载请注明原处。

相关文章推荐

objective-c设计模式 编程之道

  • 2016-05-07 17:10
  • 26.80MB
  • 下载

精通Objective-C设计模式

  • 2015-08-07 18:04
  • 26.80MB
  • 下载

Objective-C单例设计模式

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系...

《精通Objective-C设计模式》

  • 2015-08-25 09:35
  • 26.80MB
  • 下载

Objective-C编程之道iOS设计模式单例解析(1)

本文对《Objective-C编程之道iOS设计模式解析》一书中第七章的单例模式进行粗略的研究,总结了一些objective-c实现单例模式子类化相关的内容。首次发博客到CSDN首页,期待和大家多多交...

Objective-c ;单例设计模式

Objective-c 单例设计模式(Singleton) 一、什么是单例模式:(Singleton)      单例模式的意图是是的类的对象成为系统中唯一的实例,提供一个访问点,供客户类共享资源...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)