OC-常用的设计模式,引用与传值

本文介绍了Objective-C中常用的设计模式,包括单例模式、观察者模式(KVC和KVO)以及数据持久化。详细讲解了单例模式的实现步骤和完整写法,KVC作为键值编码的基础,KVO用于观察对象属性变化,最后探讨了数据持久化的实践,展示了如何在不同类之间存储和读取数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是设计模式?

设计模式是对读者经常遇到的设计问题的可再现的解决方案。
设计模式只是编写更好的面向对象程序的一种方法

简单理解就是针对某种问题所设计出来的更好的方法

常用的设计模式有哪些呢?

单例模式,观察者模式,代理模式,工厂模式……
详 情:http://blog.youkuaiyun.com/liwei3gjob/article/details/8926862

一、单例模式

什么是单例呢?

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

通俗来讲就是一个类始终只有一个实例,不管如果copy还是retain还是alloc等等,都只有一个实例,且这个实例一直存在,不能被销毁。

什么情况下使用单例?

需要确保应用中的一个特定类有且仅有一个实例(对象)
一旦单例存在,单例可在程序任何位置被访问,且一直存在。


比如:我们在做登陆的时候,通常我们会创建一个单例,来看我们这个程序是否登陆的状态,(Bool islogin),因为这个islogin在整个程序都是不断在变的,且唯一,所以我们将其作为一个单例,是非常合适的。


设计单利类的目的,限制这个类只能创建一个对象  

单例设计模式的要点:

(1) 某个类只能有一个实例。  
(2)他必须自行创建这个对象  
(3)必须自行向整个系统提供这个实例;
(4)这个方法必须是一个静态类

单例必须满足以下几个点:

1.只初始化一次
2.全局存在
3.唯一,对象不可改
4.方便获取,任何位置可访问

接下来我们来创建单例。

2.单例的创建步骤

1.首先创建一个UserCenter类
2.在.h文件中:

@interface UserCenter : NSObject 


//单例:以default,share,standard,getinstance开头

+ (UserCenter*)standardUserCenter;
 //创建返回一个单例实例化对象,只分配一次内存,无法销毁,可以在任何地方使用

//保存的信息
@property (nonatomic, copy) NSString *name;

@end
.m

//声明一个全局单例对象
static UserCenter *instance = nil;//不能让外部访问,同时放在静态块中的  

+ (UserCenter *)standardUserCenter {
    if (!instance) { //保证第一次创建成功后不再新建对象
        instance = [[UserCenter alloc] init];
    }
    return instance;
}

3.现在我们在main.m中去做实验,

我们还需要一些准备工作,创建一个Student类,在Student.m文件中,导入#import "UserCenter.h",然后重写父类init方法

- (instancetype)init {
    self = [super init];
    if (self) {
        //获取用户信息
        NSString *name = [UserCenter standardUserCenter].name;
        NSLog(@"%@", name);


    }
    return self;
}
main.m

    UserCenter *userCenter = [UserCenter standardUserCenter];
        [userCenter setName:@"yaya"];

  Student *student = [[Student alloc] init];
//打印信息:yaya

我们发现不同的对象通过单例拿到的都是同一个对象,所以,usercenter的name付的值,和student打印的值是一样的。

//当然如果你还没弄明白,你可以在main.m文件中

    UserCenter *userCenter1 = [UserCenter standardUserCenter];
    UserCenter *userCenter2 = [UserCenter standardUserCenter];
     NSLog(@"%@%@", userCenter1,userCenter2);

   //  你会发现他们的地址是一样的,所以他们其实打印是同一个对象

4.上面哪一种单例写法算简单实现,其实他还有一种最完整的写法,接下来我们看看完整单例的写法

//完整单例的实现

//声明一个全局单例对象
static UserCenter *instance = nil;

+ (UserCenter *)standardUserCenter {
    if (!instance) { //保证第一次创建成功后不再新建对象
        instance = [[UserCenter alloc] init];
    }
    return instance;
}

//重写alloc方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    if (!instance) { //保证第一次能够创建一个对象
        return [super allocWithZone:zone];
    }
    return nil;
}

//release,
- (oneway void)release{
    //将引用计数-1,但是我不让你-,万一引用计数为1,1-1= 0,内存就销毁了,但是我这块内存是不销毁,所以我就什么都不做,oneway,单项操作,没什么特别的
}
//autorelese,
- (instancetype)autorelease{

    return self;//不让你-
}
 //copy,
- (id)copyWithZone:(NSZone *)zone{
    //不让你去开辟空间
    return self;

}
 //retain
- (instancetype)retain{

    return self;

}
 //retainCount
- (NSUInteger)retainCount{
    return NSUIntegerMax;//无穷大-1,打印常量字符串,就不会去-1
}

二、观察者模式

1.了解什么是观察者模式

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

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

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

处理类的引用的时候,应注意以下几点:
每个类是否能正常接收到信息(响应动作)
信息传递时接收者是否存在
每个类是否能正常释放(不会引起循环引用)

2.观察者模式

观察者模式:kvo(设计模式,观察者模式的一种,kvo是基于kvc的一种模式,所以我们先了解kvc,kvc(key-valule-coding键-值-编码)),notification

KVC(key - value - code)键值编码

KVC,即是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问对象的属性。KVO 就是基于 KVC 实现的关键技术之一。

它主要用于更改对象状态的方法。

它的概念:
通过字符串描述来更改对象状态,这种方式称为键值编码(KVC)

它的存取方法:
属性或直接设置实例变量来直接更改对象状态


键值编码中基本调用包括两个方法:
-valueForKey:
-setValue:forKey:

接下来我们在代码示

 //kvc
       // KVC设置值时,如果属性有set方法,则优先调用set方法,如果没有则直接设置上去,get方法一样
        Person *per = [[Person alloc] init];
        //第一个参数是value,第二个参数是key(就是类的属性名称)
        [per setValue:@"yayasang" forKey:@"name"];

        Dog *dog = [[Dog alloc] init];
        [per setValue:dog forKey:@"dog"];

        //取值
        NSString *name = [per valueForKey:@"name"];
        NSLog(@"%@",name);

KVO 键值观察,建立在KVC基础上的

Key-Value Observing (KVO) 建立在 KVC 之上,它能够观察一个对象的 KVC key path 值的变化。

利用键值观察可以注册称为一个对象的观察者,在该对象的某个属性变化时收到通知。
被观察对象需要编写复合KVC标准的存取方法

举个例子,用代码观察一个 Dog 对象的 dogName属性 变化,以下是实现的三个方法(三步骤):

 //第一步:注册观察者
    //涉及到的几个参数:
     //Observer 观察者是谁
     //KeyPath监听的属性,比如监听学生的name属性
     //options :监听的内容
     //NSKeyValueObservingOptionNew,
     //NSKeyValueObservingOptionOld这两个参数的意思是监听它的新值和旧值
     //第四个参数,上下文(直接为nil)
 - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

//第二步: 响应事件
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context;

//第三步:移除观察者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

首先我们创建一个Dog类。

Dog.h

@interface Dog : NSObject

@property (nonatomic, strong)NSString *dogName;

@end

Dog.m

@implementation Dog

- (instancetype)init
{
    self = [super init];
    if (self) {
        //第一步:注册观察者
        [self addObserver:self forKeyPath:@"dogName" options: NSKeyValueObservingOptionNew |
         NSKeyValueObservingOptionOld context:nil];


    }
    return self;
}
//第二步
//响应事件
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"狗跑了");
    NSLog(@"change = %@",change);

  /*change = {
    kind = 1;
    new = kimi;
    old = zhangsan;
    */

}


- (void)dealloc
{
     NSLog(@"dog dealloc");
    //第三步:移除观察者
    [self removeObserver:self forKeyPath:@"dogName"];
    [_dogName release];
    [super dealloc];
}
@end

这就是 KVO 的作用,它通过 key path 观察对象的值,当值发生变化的时候会收到通知。

3.数据持久化

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

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

那我们可以通过什么方法将数据存入沙盒,同时也可以取出------NSUserDefaluts

下面我们来看一个例子:

main.m

        NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
        //存数据
        [userDefaults setObject:@"tom" forKey:@"name"];

        //取数据
        NSString *name2 = [userDefaults objectForKey:@"name"];
        NSLog(@"name2 = %@",name2);

        //删除数据
        [userDefaults removeObjectForKey:@"name"];
        //更新数据同步文件
        [userDefaults synchronize];

可能有同学问,那如果在不同的类里,我们也可以这样存,然后将我们存的数据取出来吗?—-of course yes

我们来创建一个Person类和Dog类

Person.h

@interface Person : NSObject
- (void)saveDate;
@end

Person.m

- (void)saveDate{
    NSUserDefaults *userDefaluts = [NSUserDefaults standardUserDefaults];
    [userDefaluts setObject:self.name forKey:@"name"];

    [userDefaluts synchronize];
}

Dog.h

@interface Dog : NSObject

- (void)loadData;

@end

Dog.m


- (void)loadData{

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSString *string = [userDefaults objectForKey:@"name"];
    NSLog(@"string = %@",string);

    [userDefaults synchronize];
}

main.m

     Person *person6 = [[Person alloc] init];
        [person6 saveDate];

       Dog *dog1 = [[Dog alloc] init];
        [dog1 loadData];

这样我们就可以达到在Person类中存,在Dog类中取,取出来的就是在Person中存的名字

4.读写文件

我们可以自己通过代码,根据一条路径自动在桌面上创建一个文件,来存取我们的数据,这个数据不是存在沙盒中的哦。

这里有个小技巧:如果你不知道你的桌面路径,可以通过在桌面上建一个文件夹,然后直接安装左键将它拖动到xcode里面,他会自动生成你的桌面路径,然后再稍微修改一下文件名就ok.

     NSArray *arr = [NSArray arrayWithObjects:@"xiaomi", @"dami", nil];
     //将数据存入文件
                [arr writeToFile:@"/Users/gaoya/Desktop/abc.txt" atomically:YES];//把arr保存在相应路径下

    //从文件里面将数据读出
                NSArray *arr1 = [NSArray arrayWithContentsOfFile:@"/Users/gaoya/Desktop/abc.txt"];
                NSLog(@"%@", arr1);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值