IOS设计架构模式:命令模式、装饰模式和原型模式

 

 

一、命令模式

1、概念

       命令对象封装了如何对目标执行指令的信息,因此客户端或调用者不必了解目标的任何细节,却仍可以对他执行任何已有的操作。通过把请求封装成对象,客 户端可以把它参数化并置入队列或日志中,也能够支持可撤销操作。命令对象将一个或多个动作绑定到特定的接收器。命令模式消除了作为对象的动作和执行它的接收器之间的绑定。

2、应用场景

IOS设计架构模式:命令模式、装饰模式和原型模式_第1张图片

       在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

3、模式中角色

IOS设计架构模式:命令模式、装饰模式和原型模式_第2张图片

抽象命令(Command): 定义命令的接口,声明执行的方法。

具体命令(ConcreteCommand): 具体命令,实现要执行的方法,它通常是“虚”的实现;通常会有接收者,并调用接收者的功能来完成命令要执行的操作。

接收者(Receiver): 真正执行命令的对象。任何类都可能成为一个接收者,只要能实现命令要求实现的相应功能。

调用者(Invoker): 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象,是存储命令对象的容器,以便进行记录和撤销操作。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

客户端(Client): 命令由客户端来创建,并设置命令的接收者。

4、Demo

接收者:

Receiver.h

#import 

@interface Receiver : NSObject {
    CGFloat _white;  // 白的
    CGFloat _alpha;  // 透明度
}

// 接收者的view
@property (nonatomic, strong) UIView *receiverView;

// 变暗的接口
- (void)mackeDarker:(CGFloat)pamameter;

// 变亮的接口
- (void)mackeLighter:(CGFloat)pamameter;

@end

Receiver.m

#import "Receiver.h"

@implementation Receiver

- (void)setReceiverView:(UIView *)receiverView {
    // 获取数值
    _receiverView = receiverView;
    UIColor *color = _receiverView.backgroundColor;
    
    [color getWhite:&_white alpha:&_alpha];
}

// 变暗的接口
- (void)mackeDarker:(CGFloat)pamameter {
    _white -= pamameter;
    // 设置取值范围
    _white = MAX(0, _white);
    
    // 设置背景色
    _receiverView.backgroundColor = [UIColor colorWithWhite:_white alpha:_alpha];
}

// 变亮的接口
- (void)mackeLighter:(CGFloat)pamameter {
    _white += pamameter;
    // 设置取值范围
    _white = MIN(1, _white);
    
    // 设置背景色
    _receiverView.backgroundColor = [UIColor colorWithWhite:_white alpha:_alpha];
}
@end

抽象命令:

CommandProtocol.h

#import 

@protocol CommandProtocol 

// 执行命令
- (void)excute;

// 撤销命令
- (void)backExcute;

@end

具体命令:

DarkerCommand.h

#import 
#import "CommandProtocol.h"
#import "Receiver.h"

@interface DarkerCommand : NSObject 

// 绑定接收器
- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter;

@end

DarkerCommand.m

#import "DarkerCommand.h"

@interface DarkerCommand ()
@property (nonatomic, strong) Receiver *receiver;
@property (nonatomic, assign) CGFloat paramter;

@end

@implementation DarkerCommand

- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter {
    self = [super init];
    if (self) {
        
        self.receiver = receiver;
        self.paramter = paramter;
    }
    return self;
}

// 执行命令
- (void)excute {
    [self.receiver mackeDarker:self.paramter];
}

// 撤销命令
- (void)backExcute {
    [self.receiver mackeLighter:self.paramter];
}

@end

LighterCommand.h

#import 
#import "CommandProtocol.h"
#import "Receiver.h"

@interface LighterCommand : NSObject 

// 绑定接收器
- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter;

@end

LighterCommand.m

#import "LighterCommand.h"

@interface LighterCommand ()
@property (nonatomic, strong) Receiver *receiver;
@property (nonatomic, assign) CGFloat paramter;

@end

@implementation LighterCommand

- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter {
    
    self = [super init];
    if (self) {
        
        self.receiver = receiver;
        self.paramter = paramter;
    }
    return self;
}

// 执行命令
- (void)excute {
    [self.receiver mackeLighter:self.paramter];
}

// 撤销命令
- (void)backExcute {
    [self.receiver mackeDarker:self.paramter];
}

@end

命令调用者只能有一个对象,采用单例模式

Invoker.h

#import 
#import "CommandProtocol.h"

@interface Invoker : NSObject

+ (instancetype)sharedInstance;

// 回退指令
- (void)goBack;

// 添加操作指令
- (void)addAndExcute:(id )command;

@end

Invoker.m

#import "Invoker.h"


@interface Invoker ()
@property (nonatomic, strong) NSMutableArray *array;  // 存储操作的
@end

@implementation Invoker

+ (instancetype)sharedInstance
{
    static Invoker *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        sharedInstance = [[self alloc] init];
        sharedInstance.array = [NSMutableArray array];
        
    });
    return sharedInstance;
}

// 回退指令
- (void)goBack {
    // 1.获取数组中的最后一个操作
    id  command = self.array.lastObject;
    
    // 2.操作调用,撤销的步骤
    [command backExcute];
    
    // 3.删除最后操作
    [self.array removeLastObject];
}

// 添加操作指令
- (void)addAndExcute:(id )command {
    // 1.把操作添加到数组
    [self.array addObject:command];
    
    // 2.让操作调用实现的协议方法
    [command excute];
}

@end

客户端:

ViewController.m

#import "ViewController.h"
#import "Receiver.h"

#import "Invoker.h"
#import "DarkerCommand.h"
#import "LighterCommand.h"

typedef enum : NSUInteger {
    kMinusBtnTag = 10, // 降低亮度
    kAddBtnTag = 20,   // 增加亮度
    kBackBtnTag = 30,  // 回退亮度
} BtnTag;

@interface ViewController ()
@property (nonatomic, strong) Receiver *receiver;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    // 接收器
    self.receiver = [[Receiver alloc] init];
    self.receiver.receiverView = self.view;

}

- (IBAction)BtnClick:(UIButton *)button {
    
    if (button.tag == kMinusBtnTag) {
        // 变暗
        DarkerCommand *command = [[DarkerCommand alloc] initWithReceiver:self.receiver paramter:0.1];
        [[Invoker sharedInstance] addAndExcute:command];
        
//        [self.receiver mackeDarker:0.1];
        
    } else if (button.tag == kAddBtnTag) {
        // 变亮
        LighterCommand *command = [[LighterCommand alloc] initWithReceiver:self.receiver paramter:0.1];
        [[Invoker sharedInstance] addAndExcute:command];
        
//        [self.receiver mackeLighter:0.1];
        
    } else if (button.tag == kBackBtnTag) {
        // 回退操作
        [[Invoker sharedInstance] goBack];
    }
}

@end

二、装饰模式

1、概念

      动态的给一个对象添加一些额外的职责,就扩展功能来说,装饰模式相比生成子类更为灵活。

2、何时使用装饰模式

  • 想要在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 想要扩展一个类的行为,却做不到。类定义可能被隐藏,无法进行子类化;或者对类的每个行为的扩展,为支持每种功能组合,将产生大量的子类
  • 对类的职责的扩展是可选的。

3、UML图

IOS设计架构模式:命令模式、装饰模式和原型模式_第3张图片

        标准的装饰模式有包括一个抽象的Component父类,它声明了一些操作,它具体的类讲进行重载以实现自己特定的操作。这个Component具体类是模式中的被装饰者,Component父类可以被细化为另一个叫做Decorator的抽象类,即装饰者抽象类。Decorator类中包含了一个Component的引用。Decorator的具体类为Component或者Decorator定义了几个扩展行为,并且会在自己的操作中内嵌Component操作。

4、装饰模式在ios中的实现

根据Objective-C的特性,有两种实现方式:

  1. 通过真正的子类实现装饰
  2. 通过分类实现装饰

第二种方式是使用了Objective-C的语言功能,通过分类向类添加行为,不必进行子类化,这并非标准的装饰模式结构,但是实现了装饰模式同样的需求。尽管使用分类来实现装饰模式跟原始风格有偏离,但是实现少量的装饰器的时候,它比真正子类方式更加轻量、更加容易。缺点是添加属性后,得runtime动态进行添加set和get方法。第一种方式则可以任意添加属性。

5、Demo:

首先举个通过子类去实现装饰模式的demo例子。

被装饰的类:

GameLoL.h

#import 

@interface GameLoL : NSObject

//  上下左右
- (void)up;
- (void)down;
- (void)left;
- (void)right;

// 选择与开始的操作
- (void)select;
- (void)start;

// 按钮
- (void)commandA;
- (void)commandB;
- (void)commandC;
- (void)commandD;

@end

GameLoL.m

#import "GameLoL.h"

@implementation GameLoL

- (void)up {
    NSLog(@"up");
}

- (void)down {
    NSLog(@"down");
}

- (void)left {
    NSLog(@"left");
}

- (void)right {
    NSLog(@"right");
}

- (void)select {
    NSLog(@"select");
}

- (void)start {
    NSLog(@"start");
}

- (void)commandA {
    NSLog(@"commandA");
}

- (void)commandB {
    NSLog(@"commandB");
}

- (void)commandC {
    NSLog(@"commandC");
}

- (void)commandD {
    NSLog(@"commandD");
}

@end

装饰者:

GameDecortor.h

#import 
#import "GameLoL.h"

@interface GameDecortor : NSObject
//  上下左右
- (void)up;
- (void)down;
- (void)left;
- (void)right;

// 选择与开始的操作
- (void)select;
- (void)start;

// 按钮
- (void)commandA;
- (void)commandB;
- (void)commandC;
- (void)commandD;

@end

GameDecortor.m

#import "GameDecortor.h"

@interface GameDecortor ()
@property (nonatomic, strong) GameLoL *gameLoL;
@end

@implementation GameDecortor

- (instancetype)init {
    self = [super init];
    if (self) {
        self.gameLoL = [[GameLoL alloc] init];
    }
    return self;
}

// GameLoL里面所有的方法
- (void)up {
    [self.gameLoL up];
}

- (void)down {
    [self.gameLoL down];
}

- (void)left {
    [self.gameLoL left];
}

- (void)right {
    [self.gameLoL right];
}

- (void)select {
    [self.gameLoL select];
}

- (void)start {
    [self.gameLoL start];
}

- (void)commandA {
    [self.gameLoL commandA];
}

- (void)commandB {
    [self.gameLoL commandB];
}

- (void)commandC {
    [self.gameLoL commandC];
}

- (void)commandY {
    [self.gameLoL commandD];
}
@end

 具体实现的子类:

CheatGameDecotor.h

#import 
#import "GameDecortor.h"

@interface CheatGameDecotor : GameDecortor

// 作弊器
- (void)cheat;

@end

CheatGameDecotor.m

#import "CheatGameDecotor.h"

@implementation CheatGameDecotor

- (void)cheat {
    // 作弊操作
    [self up];
    [self up];
    
    [self down];
    [self down];
    
    [self commandA];
    [self commandA];
}

@end

CoinGameDecorator.h

#import "GameDecortor.h"

@interface CoinGameDecorator : GameDecortor

// 游戏币
@property (nonatomic, assign) NSInteger coin;

@end

CoinGameDecorator.m

#import "CoinGameDecorator.h"

@implementation CoinGameDecorator

@end

接着举一个使用分类扩展属性和方法的例子

GameLoL+Coin.h

#import "GameLoL.h"

@interface GameLoL (Coin)
// 游戏币
@property (nonatomic, assign) NSInteger coin;

@end

GameLoL+Coin.m

#import "GameLoL+Coin.h"
#import 

static const NSString *_coinStr = @"_coinStr";
@implementation GameLoL (Coin)

- (void)setCoin:(NSInteger)coin {
    objc_setAssociatedObject(self, (__bridge const void *)_coinStr, @(coin), OBJC_ASSOCIATION_ASSIGN);
    
}

- (NSInteger)coin {
   NSNumber *number = objc_getAssociatedObject(self, (__bridge const void *)_coinStr);
    
    return number.integerValue;
}

@end

GameLoL+Cheat.h

#import "GameLoL.h"

@interface GameLoL (Cheat)
// 作弊器
- (void)cheat;

//- (void)up;
@end

GameLoL+Cheat.m

#import "GameLoL+Cheat.h"

@implementation GameLoL (Cheat)

- (void)cheat {
    // 作弊操作
    [self up];
    [self up];
    
    [self down];
    [self down];
    
    [self commandA];
    [self commandA];
}

//- (void)up {
//    NSLog(@"LOLOLOLOL123123");
//}

@end

三、原型模式

1、概念

        原型模式是指使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象。也就是提供一个快速复制对象的快捷方式。

2、UML图

IOS设计架构模式:命令模式、装饰模式和原型模式_第4张图片

        从上图可以看到,Prototype类中包括一个clone方法,Client调用其拷贝方法clone即可得到实例,不需要手工去创建实例。ConcretePrototype1和ConcretePrototype2为Prototype的子类,实现自身的clone方法,如果Client调用ConcretePrototype1的clone方法,将返回ConcretePrototype1的实例。简单写个demo如下:

协议类:

PrototypeCopyProtocol.h

#import 

@protocol PrototypeCopyProtocol 

// 复制自己
- (id)clone;

@end

原型类实现协议

StudentModel.h

#import 
#import "PrototypeCopyProtocol.h"

@interface StudentModel : NSObject 

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

- (id)clone;

@end

StudentModel.m

#import "StudentModel.h"

@implementation StudentModel

- (id)clone {
    StudentModel *student = [[[self class] alloc] init];
    student.name = self.name;
    student.age = self.age;
    student.address = self.address;
    student.totalScore = self.totalScore;
    
    return student;
}
@end

客户端根据原型实例进行复制

ViewController.h

#import "ViewController.h"
#import "StudentModel.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    // 学生1
    StudentModel *stu1 = [[StudentModel alloc] init];
    stu1.name = @"张三";
    stu1.age = @(18);
    stu1.address = @"帝都";
    stu1.totalScore = @(99);
    
    // 学生2
//    StudentModel *stu2 = [[StudentModel alloc] init];
//    stu2.name = @"李四";
//    stu2.age = @(18);
//    stu2.address = @"帝都";
//    stu2.totalScore = @(99);
    
    // 学生3
    StudentModel *stu3 = [stu1 clone];
    stu3.name = @"王五";
    
}

@end

3、NSCodeing协议:深拷贝和浅拷贝

Cocoa Touch框架为NSObject的派生类提供了实现深复制的协议,即NSCopying协议,提供深复制的NSObject子类,需要实现NSCopying协议的方法(id)copyWithZone:(NSZone *)zone。NSObject有一个实例方法(id)copy,这个方法默认调用了[self copyWithZone:nil],对于引用了NSCopying协议的子类,必须实现(id)copyWithZone:(NSZone *)zone方法,否则将引发异常,异常信息如下:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Prototype copyWithZone:]: unrecognized selector sent to instance 0x100114e60'

原型类:

BaseCopyObject.h

@interface BaseCopyObject : NSObject 

// 子类不要重载
- (id)copyWithZone:(NSZone *)zone;

// 子类去实现
- (void)copyOperationWithObject:(id)object;

@end

BaseCopyObject.m

#import "BaseCopyObject.h"

@implementation BaseCopyObject

- (id)copyWithZone:(NSZone *)zone {
    BaseCopyObject *copyObject = [[self class] allocWithZone:zone];
    // 赋值操作
    [self copyOperationWithObject:copyObject];
    
    return copyObject;
}

- (void)copyOperationWithObject:(id)object {
}

@end

原型子类:

ClassModel.h

#import "BaseCopyObject.h"

@interface ClassModel : BaseCopyObject

@property (nonatomic, copy) NSString *className;
@property (nonatomic, copy) NSArray *students;

@end

ClassModel.m

#import "ClassModel.h"

@implementation ClassModel
- (void)copyOperationWithObject:(ClassModel *)object {
    object.className = self.className;
    // 浅拷贝
//    object.students = self.students;
    
    // 深拷贝
    object.students = [[NSArray alloc] initWithArray:self.students copyItems:YES];


}

@end

客户端调用原型子类:

ViewController.m

#import "ViewController.h"
#import "StudentModel.h"
#import "ClassModel.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    StudentModel *stu1 = [StudentModel new];
    stu1.name = @"张三";
    
    StudentModel *stu2 = stu1.copy;
    
    // classModel
    ClassModel *class1 = [[ClassModel alloc] init];
    class1.className = @"ban ji 1";
    class1.students = @[stu1, stu2];
    
    ClassModel *class2 = class1.copy;
    NSLog(@"%@ ----%@", class1, class2);
    
    NSLog(@"%@", class1.students);
    NSLog(@"%@", class2.students);
    
}
@end

 

运行结果:IOS设计架构模式:命令模式、装饰模式和原型模式_第5张图片

IOS设计架构模式:命令模式、装饰模式和原型模式_第6张图片

  • 浅复制:只复制了指针值,并没有复制指针指向的资源(即没有创建指针指向资源的副本),复制后原有指针和新指针共享同一块内存。
  • 深复制:不仅复制了指针值,还复制了指针指向的资源。

下面的示意图左边为浅复制,右边为深复制。

IOS设计架构模式:命令模式、装饰模式和原型模式_第7张图片

4、assigncopy retain

我们还是通过一个示例来说明这三者的区别,定义一个类,类里面只有三个属性,如下所示:

.h文件

@interface Test : NSObject

 

@property (nonatomic, copy)NSMutableString *strName;

@property (nonatomic, assign)NSMutableString *strName1;

@property (nonatomic, retain)NSMutableString *strName2;

.m文件

Test *t = [[Testalloc] init];

        NSMutableString *strTest = [[NSMutableStringalloc] initWithString:@"abc"];

        NSLog(@"strTest retainCount:%ld strTest:%p %@",[strTest retainCount],strTest,strTest);

        t.strName1 = strTest;  // assign

        NSLog(@"after assign:  strTest retainCount:%ld t.strName1:%p %@ ",[strTest retainCount],t.strName1,t.strName1);

        t.strName = strTest;  // copy

        NSLog(@"after copy:  strTest retainCount:%ld t.strName:%p %@ ",[strTest retainCount],t.strName,t.strName);

        t.strName2 = strTest;  // retain

        NSLog(@"after retain:  strTest retainCount:%ld t.strName2:%p %@ ",[strTest retainCount],t.strName2,t.strName2);

运行结果:

start:  strTest retainCount:1 strTest:0x1001157f0 abc

after assign:  strTest retainCount:1 t.strName1:0x1001157f0 abc 

after copy:  strTest retainCount:1 t.strName:0x100400460 abc 

after retain:  strTest retainCount:2 t.strName2:0x1001157f0 abc 

首先,咱们分析一下这行代码:NSMutableString *strTest = [[NSMutableStringalloc] initWithString:@"abc"];这行代码实际上进行了两个操作:

  • 在栈上分配一段内存用来存储strTest,比如地址为0xAAAA,内容为0x1001157f0
  • 在堆上分配一段内存用来存储@"abc",地址为0x1001157f0,内容为abc。

  现在,咱们针对刚才示例的输出结果来分别对assign、copy和retain进行说明:

  assign:默认值,应用assign后,t.strName1和strTest具有相同的内容0x1001157f0,并且retainCount没有增加,可以理解t.strName1是strTest的别名;

  copy:应用copy后,会在堆上重新分配一段内存来存储@"abc",地址为0x100400460,同时也会在栈上分配一段内存用来存储t.strName,比如地址为0xBBBB,内容为0x100400460,这时strTest管理0x1001157f0这段内存;t.strName管理0x100400460这段内存。t.strName和strTest的retainCount均为1。

  retain:应用retain后,可以看到retainCount增加了1,说明在栈上重新分配了一段内存来存储t.strName2,比如地址为0xCCCC,内容为0x1001157f0。此时,strTest和t.strName2共同管理0x1001157f0这段内存。

  想必这样介绍完,大家对于这三个属性应该是了解的比较清楚了。这里再顺便说一下atomic和nonatomic,这两个属性用来决定编译器生成的getter和setter是否为原子操作。

  atomic:默认值,提供多线程安全。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数在操作前会加锁。

  nonatomic:禁用多线程的变量保护,提高性能。

  atomic是OC中使用的一种线程保护技术,用来防止在写操作未完成的时候被另外一个线程读取,造成数据错误。但是这种机制是耗费系统资源的,所以如果没有使用多线程的通讯编程,那么nonatomic是一个非常好的选择。

【小思考】:将本示例中的所有NSMutableString替换成NSString后,结果是不一样的,大家可以试验一下,然后思考这是为什么?

5、详解深拷贝和浅拷贝

像NSString、NSDictionary这些类,本身已经实现了copyWithZone:(NSZone *)zone方法,直接使用如[NSString copy]调用即可。在复制后得到的副本,又可以分为可变副本(mutable copy)和不可变副本(immutable copy)。通常在NSCopying协议规定的方法copyWithZone中返回不可变副本,在NSMutableCopying协议规定的方法mutableCopyWithZone中返回可变副本,然后调用copy和mutableCopy方法来得到相应的不可变和可变副本。

NSString类已经遵循NSCopying协议及NSMutableCopying协议,下面还是通过示例来进行测试。

示例一:

NSString *strSource = [NSStringstringWithFormat:@"I am %@",@"ligf"];

// 使用copy方法,strSource和strCopy内存地址一致,strSource引用计数加1

NSString *strCopy = [strSource copy];
NSLog(@"原始字符串:%p,%@",strSource,strSource);
NSLog(@"复制字符串:%p,%@",strCopy,strCopy);

输出结果:

原始字符串:0x1001156c0,I am ligf

复制字符串:0x1001156c0,I am ligf

【结论】:

  由[strSource copy]得到的strCopy,两者内存地址一致,由于copy返回的是不可变副本,系统只生成一份内存资源,此时的copy只是浅复制,和retain作用一样。(上一小节小思考里面留下的问题就是这个原因)

示例二:

NSString *strSource = [NSStringstringWithFormat:@"I am %@",@"ligf"];

// 使用mutableCopy方法,strSource和strCopy内存地址不一致,两者的引用计数均为1

NSString *strCopy = [strSource mutableCopy];
NSLog(@"原始字符串:%p,%@",strSource,strSource);
NSLog(@"复制字符串:%p,%@",strCopy,strCopy);

输出结果:

原始字符串:0x1001156c0,I am ligf

复制字符串:0x100114fb0,I am ligf

【结论】:

  由[strSource mutableCopy]得到的strCopy,两者内存地址不一致,由于mutableCopy返回的是可变副本,系统生成了新的内存资源,此时的mutableCopy是深复制。

示例三:

NSMutableString *strSource = [NSMutableStringstringWithFormat:@"I am %@",@"ligf"];

// NSMutableString使用copy方法,strSource和strCopy内存地址不一致,两者的引用计数均为1

NSMutableString *strCopy = [strSource copy];
NSLog(@"原始字符串:%p,%@",strSource,strSource);
NSLog(@"复制字符串:%p,%@",strCopy,strCopy);
[strCopy appendString:@"hello"];

输出结果:

原始字符串:0x100115470,I am ligf

复制字符串:0x100115690,I am ligf

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'

【结论】:

  由[strSource copy]得到的strCopy,两者内存地址不一致,即是copy对NSMutableString类型进行了深复制,当尝试修改strCopy里面的值时,发现报错了,无法修改,可以确定副本strCopy是不可变副本。

【总的结论】:

  对于系统中已经实现的同时支持NSCopying协议和NSMutableCopying协议的NSString、NSDictionary等,copy总是返回不可变副本,mutableCopy总是返回可变副本。

6、何时用原型模式

  • 需要创建的对象应独立于其类型与创建方式。
  • 要实例化的类是在运行时决定的。
  • 不想要与产品层次相对应的工厂层次。
  • 不同类的实例间的差异仅是状态的若干组合。因此复制相应数量的原型比手工实例化更加方便。
  • 类不容易创建,比如每个组件可以把其他组件作为子节点的组合对象。复制已有的组合对象并对副本进行修改会更加容易。

  以下两种特别常见的情形,我们会想到用原型模式:

  • 有很多的相关的类,其行为略有不同,而且主要差异在于内部属性,如名称等;
  • 需要使用组合(树)对象作为其他对象的基础,比如,使用组合对象作为组件来构建另一个组合对象。

你可能感兴趣的:(IOS架构设计)