iOS课程观看笔记(九)---设计模式

在这里插入图片描述

六大设计原则(知识点盲区)

在这里插入图片描述

单开口,依里迪

1单一职责原则

一个类只负责一件事
例如:UIView和CALayer

2开闭原则

对修改关闭,对扩展开放
例如:定义好一个类,尽量减少对它的修改,同时把扩展打开

3接口隔离原则

使用多个专门的协议,而不是一个庞大臃肿的协议
协议中的方法应当尽量少

比如:UITableView,提供了UITableViewDataSource和UITableViewDelegate两个协议

4依赖倒置原则

抽象不应该依赖于具体实现,具体实现可以依赖于抽象

5里式替换原则

父类可以被子类无缝替换,且原有功能不受任何影响

例如,在KVO监听中,系统重写了setter方法,然后将isa指针指向其子类,悄无声息的子类替换掉父类。

6迪米特法则

一个对象应当对其他对象有尽可能少的了解
从而达到高内聚、低耦合


责任链设计模式

在这里插入图片描述

在这里插入图片描述
一个类A,其有一个成员变量B,该成员变量的类型跟A类型一样,从而构成责任链

在这里插入图片描述

责任链模式的应用:响应链模式

- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;   
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;

参考资料:
iOS 责任链模式

注意:

责任链模式与链式编程的概念区别

问:什么是链式(函数式)编程?

通过高阶函数以点为连接将多个函数连接在一起完成参数传递和复杂的操作!
例如在Masonry中的这样的代码
make.right.equalTo(self.right).insets(kPadding);


桥接设计模式

问:什么是桥接?

或者,直接问一道需要用桥接模式解决问题的业务题
在这里插入图片描述

一个列表,有三套并存的数据,使用哪一套,后台控制
也可能不是三套,而是30套,如何解决?

解决方法:建立一个抽象父类,所有的网络数据处理继承抽象父类,从而调用哪一套直接调用。

在这里插入图片描述
使用上面方法,建立两个抽象类,且通过抽象类B是抽象类A的一个成员变量的方法,将两个抽象类建立连接。
从而可以使得两边的子类A和子类B相互关联。
上述方法,有9种组合


适配器设计模式

一个现有类需要使用变化的问题
假如有一个类A,是在N年前写的,很多地方都要用,但成熟且很久没人去修改里面的代码。现有一个新功能,需要修改原有类A,怎么做?
直接修改类A,那么其他用到类A的地方有可能出错,因此,我们不直接修改类A,才有适配器模式进行修改。

适配器有两种方法:
对象适配器
类适配器

下面叙述对象适配器
在这里插入图片描述
被适配对象就是原有对象A
适配对象就是需要新建的对象,假如是对象B
那么,新建对象B里有一个成员变量,是被适配对象A,从而将两者建立联系。

在新建对象B里:
在这里插入图片描述


单例模式

#import <Foundation/Foundation.h>
@interface Mooc : NSObject
+ (id)sharedInstance;
@end


#import "Mooc.h"
@implementation Mooc
+ (id)sharedInstance
{
    // 静态局部变量
    static Mooc *instance = nil;
    
    // 通过dispatch_once方式 确保instance在多线程环境下只被创建一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 创建实例
        instance = [[super allocWithZone:NULL] init];
    });
    return instance;
}

// 重写方法【必不可少】
+ (id)allocWithZone:(struct _NSZone *)zone{
    return [self sharedInstance];
}

// 重写方法【必不可少】
- (id)copyWithZone:(nullable NSZone *)zone{
    return self;
}
@end

需要注意的地方:

+sharedInstance方法中,
instance = [[super allocWithZone:NULL] init];
而不是
instance = [[self allocWithZone:NULL] init];

这是因为,如果用户第一次创建单例是通过[Mooc allocWithZone:nil];创建的,那么会是一个死循环

在这里插入图片描述

instance = [[super alloc] init];也不可以

在这里插入图片描述结果:
在这里插入图片描述

源码分析:

+ (id) alloc
{
  return [self allocWithZone: NSDefaultMallocZone()];
}

可以看出,alloc内部调用了 allocWithZone: NSDefaultMallocZone()

使用Clang命令进行编译:
instance = [[super alloc] init];
编译后:

(*instance) = ((Mooc ()(id, SEL))(void *)objc_msgSend)((id)((Mooc ()(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)(&(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getMetaClass(“Mooc”))}, sel_registerName(“alloc”)), sel_registerName(“init”));

objc_msgSend(objc_msgSendSuper({self, [Mooc super]}, @selector(alloc)), @selector(init));

instance = [[super allocWithZone:NULL] init];
编译后:

(*instance) = ((Mooc ()(id, SEL))(void *)objc_msgSend)((id)((Mooc ()(__rw_objc_super *, SEL, struct _NSZone *))(void *)objc_msgSendSuper)(&(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getMetaClass(“Mooc”))}, sel_registerName(“allocWithZone:”), (struct _NSZone *)__null), sel_registerName(“init”));

objc_msgSend(objc_msgSendSuper({self, [Mooc super]}, @selector(allocWithZone)), @selector(init));

感觉,看不出什么

继续分析:

+ (id)sharedInstance
{
    // 静态局部变量
    static Mooc *instance = nil;
    
    // 通过dispatch_once方式 确保instance在多线程环境下只被创建一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 创建实例
        NSLog(@"--1--");
        instance = [[super allocWithZone:NULL] init];
    });
    return instance;
}

// 重写方法【必不可少】
+ (id)allocWithZone:(struct _NSZone *)zone{
	NSLog(@"--2--");
    return [self sharedInstance];
}

调用:
Mooc *mooc1 = [Mooc allocWithZone:nil];
NSLog(@"%p", mooc1);

打印结果:
--2--
--1--

而使用

instance = [[super alloc] init];
打印结果:
--2--
--1--
--2--

崩溃

首先,我们要知道:

+ (id)allocWithZone:(struct _NSZone *)zone
{
	return [self sharedInstance];
}

是重写的自己的allocWithZone:
并没有重写父类的allocWithZone:方法。

为了观察Mooc的父类allocWithZone和alloc的重写情况,我们给Mooc创建了一个父类MoocFather

#import "MoocFather.h"
@implementation MoocFather
+ (instancetype)alloc
{
    NSLog(@"%s", __func__);
    return [super alloc];
}

+ (id)allocWithZone:(struct _NSZone *)zone{
    NSLog(@"%s", __func__);
    return [super allocWithZone:zone];
}
@end

在使用[[super allocWithZone:NULL] init];的时候
在这里插入图片描述

有进入其父类的allocWithZone:方法,调用成功

在使用[[super allocWithZone:NULL] init];的时候,直接调用成功,没有再进入allocWithZone:方法

而在使用[[super alloc] init];的时候
在这里插入图片描述[super alloc]进入其父类的+alloc方法,再进去是NSObject的+alloc方法。
消息接收者是Mooc,也就是最后的调用者还是Mooc。也就是最后调用的是[Mooc alloc];
而[Mooc alloc];调用的是[Mooc allocWithZone:NSDefaultMallocZone()];
由于我们重写了allocWithZone:,然后进入的是[self sharedInstance];从而造成了循环引用。

也就是:
在使用[[super alloc] init];的时候,再次进入allocWithZone:方法,从而再次调用sharedInstance方法,又进入allocWithZone:调用instance = [[super alloc] init];,从而形成循环。


网上还有其他单例的写法,我们调几个进行验证:
方法二:

#import "Singleton1.h"
static Singleton1 *_instance = nil;

@implementation Singleton1
+ (id)sharedInstance
{
	return [[self alloc] init];
	或者
	return [[self allocWithZone:NULL] init];
	或者
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}

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


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

不同点1:
方法一将static Mooc *instance = nil;写在+ (id)sharedInstance方法里面,instance是静态局部变量。
方法二将static Singleton1 *_instance = nil;写在全局,_instance是静态全局变量。

不同点2:

方法一:
- (id)copyWithZone:(nullable NSZone *)zone{
    return self;
}

方法二:
- (id)copyWithZone:(nullable NSZone *)zone{
    return _instance;
}

由于copyWithZone:是对象方法,首先,要创建对象才能调用copyWithZone:
创建对象的方法有两种:
sharedInstance和alloc
而alloc内部调用的是allocWithZone:
所以,在创建对象的时候,不管使用的是sharedInstance或者allocWithZone:,其实已经是单例对象了。

因此,方法copyWithZone:中,return self,其实就是谁调用这个方法,return谁
而调用这个方法的创建对象无法是sharedInstance或者allocWithZone:
这两个方法的返回值都是return _instance;,也就是[_instance copyWithZone:]
在copyWithZone:中,self 就是 _instance
也就是,return _instance 和 return self一样的。

self,谁调用就是谁
在类方法里调用self,就是类对象
在对象方法里调用self,就是对象。

同理:

- (id)copyWithZone:(nullable NSZone *)zone{
    return [Singleton1 shareInstance];
}

这种写法,与前两种写法等价。

方法二中

+ (id)sharedInstance
{
	return [[self alloc] init];
	或者
	return [[self allocWithZone:NULL] init];
	或者
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}

三种方法都可以,调用sharedInstance等于调用到了alloc,alloc内部调用了allocWithZone:,在allocWithZone:里面进行了仅一次创建。

总结:

- (id)copyWithZone:(nullable NSZone *)zone{	
	三种写法都可以
    return self;
    return [Singleton1 shareInstance];
    return _instance;
}
+ (id)sharedInstance
{
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
}

两个方法,有一个进行一次dispatch_once_t即可,另外一个可以调取另外一个dispatch_once_t后的方法。

需要注意的是,一个是self,一个super。不要产生循环引用问题。


命令模式

行为参数化

例如:微博,有很多地方可以点赞、转发功能,因此,我们可以将点赞、转发功能进行封装,这样在调用的时候直接调用即可。

从而降低代码重合度

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值