前言:
由于之前写的时候匆忙,本篇博客有误,今天进行了修正。希望之前没几个人看,以免误人子弟。
另:希望iOS的各位一起来讨论iOS的技术。欢迎留言,不定时回复。
1.设计模式分类
所谓设计模式,是前人在开发过程中总结的经验。各自有各自的使用情况。分类条件不同 设计模式的分类也不尽相同。编程之道中大致分类如下
2.原型设计模式
首先从简单的入手。看看原型模式吧。学习Javascript的时候有一个ProtoType 翻译过来就是原型,那么什么是原型呢?
举个生活中的例子,假设你要做生意 要发名片 那么 你就需要先设计一个名片然后打印N多份然后发送给客户。即copy。
在编程语言当中,经常有这样的情况,如我想操作某个对象,但是我又不想把他的内容改变了,这时候需要先保存这个对象。或者是我需要多个一样的对象。
2.1 代码设计
以Person类为例
#import
@interface Person : NSObject
@property (nonatomic,copy) NSString * name;
@property (nonatomic,copy) NSString * sex;
@property (nonatomic,copy) NSString * experience;
//显示
-(void)display;
-(void)test1;
-(void)test2;
@end
@implementation Person
-(void)display
{
NSLog(@"姓名:%@",_name);
NSLog(@"性别:%@",_sex);
NSLog(@"工作经验%@",_experience);
}
@end
Person * p = [[Person alloc]init];
p.name = @"name1";
p.sex = @"男";
p.experience = @"一年";
[p display];
Person * p1 = [[Person alloc]init];
p1.name = @"name1";
p1.sex = @"男";
p1.experience = @"一年";
[p1 display];
创建的过程是一个耗费cpu的过程 想象一下 足够大的数据量下都进行创建 系统性能会如何呢?而且如果对变量设置 基本上都是一样代码 造成代码冗余。
于是原型设计模式就很有必要了。
2.2 对代码进行重构
@interface Person : NSObject
@property (nonatomic,copy) NSString * name;
@property (nonatomic,copy) NSString * sex;
@property (nonatomic,copy) NSString * experience;
//提供原型类的克隆接口
-(instancetype)clone;
//显示
-(void)display;
@end
@implementation Person
-(void)display
{
NSLog(@"姓名:%@",_name);
NSLog(@"性别:%@",_sex);
NSLog(@"工作经验%@",_experience);
}
-(instancetype)clone
{
return [self copy]; (1)标记
}
-(id)copyWithZone:(NSZone *)zone
{
Prototype * p = [[[self class] alloc]init];
p.name = self.name;
return p;
}
@end
为什么要写下面这个方法呢?
答案: 以上代码中红色标记位置,是调用该类的copy方法,这个方法是从NSObject继承而来,而这个方法内部会调用copyWithZone方法,这个方法是NSCopying协议里面的,对于任何继承自NSObject的类而言,如果需要使用copy mutablecopy 则需要重载copyWithZone方法或者mutableCopyWithZone方法。
系统类之所以需要不需要重写这两个方法,是因为内部已经重载了。
2.3 什么是原型设计模式
原型设计模式:用原型实例指定创建对象的种类 并通过拷贝这个对象来得到新的对象其类图如下:
从上面可以看到 当前一个原型的类中提供一个用于复制的接口 使用这个类创建出实例后,每个实例都有一个clone的接口 于是用这个实例就可以拷贝出新的对象。
2.4 iOS中的原型设计模式
iOS中已经实现了原型设计模式,提供了用于复制的接口。由于iOS不支持接口,将拷贝的接口做成了协议。NSCopying。
看系统内部NSCopying 的声明:
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
如果需要用到拷贝 必须遵守这个协议 在类内实现copyWithZone方法。
但是 有没有发现,fundation中的好多对象都已经遵守了 而且还多了一个NSMutableCopying。这个暂时不讨论,只讨论NSCopying。
2.5 iOS中原型设计模式的使用情形:
需要创建的对象应独立于其类型和创建方式
要实例化的类是在运行时决定的
不想要与产品层次相对应的工厂层次 (不太懂)
不同类的实例间的差异仅仅是状态的若干组合 因此复制相应数量的原型比手工实例化更方便。
该模式的最低限度是生成对象的真实副本 以用作同一环境下其他相关事物的基础
那么现在应该清楚iOS中的NSCopying 和NSMutableCopying是运用的原型设计模式。那么我们来讨论下。
首先,涉及到深拷贝和浅拷贝的内容。
浅拷贝:如果当前拷贝只是复制了对象的指针,而没有内容的复制 被称为浅拷贝。如下图。
深拷贝:如果当前拷贝不仅仅复制了对象的指针,而且在内存开辟了内存空间,以存储拷贝的内容,被称之为深拷贝。如下图。
2.6 cocoa Touch框架中的对象复制
coucoa touch框架为NSObject派生类提供了两个协议。NSCopying NSMutableCopying。
以iOS中字符串的复制为例 讨论深拷贝和浅拷贝。
-(void)test1
{
NSString * str = @"abc";
NSLog(@"字符串地址%p",str);
NSString * str1 = [str copy];
NSLog(@"不可变字符串copy后地址%p",str1);
NSString * str2 = [str mutableCopy];
NSLog(@"不可变字符串mutableCopy后地址%p",str2);
NSMutableString * strM = [[NSMutableString alloc]initWithString:@"def"];
NSMutableString * strM1 = [strM copy];
NSLog(@"可变字符串copy后地址%p",strM1);
NSString * strM2 = [strM mutableCopy];
NSLog(@"可变字符串的mutablecopy地址%p",strM2);
}
console的信息:
2015-11-13 18:13:06.162 0-5 原型模式[2344:187326] 字符串地址0x1000020c0
2015-11-13 18:13:06.164 0-5 原型模式[2344:187326] 不可变字符串copy后地址0x1000020c0
2015-11-13 18:13:06.165 0-5 原型模式[2344:187326] 不可变字符串mutableCopy后地址0x1004046e0
2015-11-13 18:13:06.165 0-5 原型模式[2344:187326] 可变字符串copy后地址0x66656435
2015-11-13 18:13:06.165 0-5 原型模式[2344:187326] 可变字符串的mutablecopy地址0x1004048d0
由以上结果可以看出,NSString的copy得到的字符串地址相同,mutable copy后得到字符串地址不同,NSMutableString copy得到字符串地址不同,mutable copy得到字符串地址不同。
结论:NSString的copy是浅拷贝,而NSMutableString的copy mutable copy NSString的mutableCopy都是深拷贝
是否是深拷贝的唯一判定标准是:是否通过拷贝得到了新的对象,而非仅仅拷贝了地址。
下面讨论拷贝得到的字符串的可变性。
-(void)test2
{
//讨论字符串复制后的可变性
NSString * str = @"abc";
NSMutableString * str1 = [str copy];
NSMutableString * str2 = [str mutableCopy];
NSMutableString * strM = [[NSMutableString alloc]initWithString:@"abc"];
NSMutableString * strM1 = [strM copy];
NSMutableString * strM2 = [strM mutableCopy];
[str1 appendString:@"a"];//1
[str2 appendString:@"a"];//2
[strM1 appendString:@"a"];//3
[strM2 appendString:@"a"];//4
}
做标注的1,3处 会报错误如下
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0x63626135'
尝试将不可识别的消息发送给实例。
也就是说 1,3得到的字符串是不可变的。
结论:无论是NSString 还是NSMutable String 通过copy得到的字符串 都是不可变的。通过mutableCopy得到的字符串 都是可变的。
那么在oc中是仅仅是字符串是这样子的吗?
下面实例给出数组的测试用例
-(void)test3
{
NSArray * arr = @[@"2",@"1"];
NSMutableArray * arrM = [NSMutableArray arrayWithArray:arr];
NSMutableArray * arr1 = [arr copy];
NSMutableArray * arr2 = [arr mutableCopy];
NSLog(@"NSArray地址%p",arr);
NSLog(@"NSArray copy地址%p",arr1);
NSLog(@"NSArray mutableCopy地址%p",arr2);
NSMutableArray * arrM1 = [arrM copy];
NSMutableArray * arrM2 = [arrM mutableCopy];
NSLog(@"NSMutableArray copy地址%p",arrM1);
NSLog(@"NSMutableArray mutableCopy地址%p",arrM1);
}
console:
2015-11-13 18:29:02.416 0-5 原型模式[2427:192975] NSArray地址0x10030aef0
2015-11-13 18:29:02.416 0-5 原型模式[2427:192975] NSArray copy地址0x10030aef0
2015-11-13 18:29:02.417 0-5 原型模式[2427:192975] NSArray mutableCopy地址0x10030c0e0
2015-11-13 18:29:02.417 0-5 原型模式[2427:192975] NSMutableArray copy地址0x10020cc80
2015-11-13 18:29:02.417 0-5 原型模式[2427:192975] NSMutableArray mutableCopy地址0x10020d1d0
Program ended with exit code: 0
由以上结果可知iOS中不可变数组的copy为浅拷贝,不可变数组的mutablecopy和可变数组的copy和mutableCopy都是深拷贝。
可变性讨论,给出数组复制后的可变性测试用例
-(void)test4
{
NSArray * arr = @[@"2",@"1"];
NSMutableArray * arrM = [NSMutableArray arrayWithArray:arr];
NSMutableArray * arr1 = [arr copy];
NSMutableArray * arr2 = [arr mutableCopy];
NSMutableArray * arrM1 = [arrM copy];
NSMutableArray * arrM2 = [arrM mutableCopy];
[arr1 addObject:@"abc"];//1
[arr2 addObject:@"abc"];//2
[arrM1 addObject:@"abc"];//3
[arrM2 addObject:@"abc"];//4
}
在以上标注的1,3的情况下 console信息如下:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x100205570'
3.结论:
1.NSMutableArray类对象的拷贝都是深拷贝,NSArray 的copy为浅拷贝,mutable copy是深拷贝,得到的新对象的可变性要看是copy还是mutable copy。
2.copy的情况下得到的对象不可变,mutablecopy的情况下得到的对象可变。
继续测试NSDictionary。
测试用例1:
-(void)testDictionary
{
NSDictionary * d = [NSDictionary dictionary];
NSDictionary * d2 = [d copy];
NSMutableDictionary * d3 = [d mutableCopy];
NSMutableDictionary * dictM = [NSMutableDictionary dictionary];
NSMutableDictionary * dictM2 = [dictM copy];
NSMutableDictionary * dictM3 = [dictM mutableCopy];
NSLog(@"地址%p",d);
NSLog(@"copy地址%p",d2);
NSLog(@"mutablecopy地址%p",d3);
NSLog(@"地址%p",dictM);
NSLog(@"copy地址%p",dictM2);
NSLog(@"mutablecopy地址%p",dictM3);
}
执行结果:
016-01-06 20:18:45.680 1fundation框架[1633:148875] 地址0x1002005b0
2016-01-06 20:18:45.680 1fundation框架[1633:148875] copy地址0x1002005b0
2016-01-06 20:18:45.680 1fundation框架[1633:148875] mutablecopy地址0x100103680
2016-01-06 20:18:45.681 1fundation框架[1633:148875] 地址0x100103a40
2016-01-06 20:18:45.681 1fundation框架[1633:148875] copy地址0x1002005b0
2016-01-06 20:18:45.681 1fundation框架[1633:148875] mutablecopy地址0x100103c60
Program ended with exit code: 0
可变性测试:
在以上代码的基础上添加:
[d2 setObject:@"a" forKey:@"aa"];(1)
[d3 setObject:@"a" forKey:@"aa"];
[dictM2 setObject:@"a" forKey:@"aa"];(2)
[dictM3 setObject:@"a" forKey:@"aa"];
1,2处会报错误,也就是说 NSDictionary NSMutableDictionary的copy不可变 mutable copy可变。
结论:
(1) fundation框架中 对象的copy不可变,mutable copy是可变的。
(2)不可变对象的copy是浅拷贝,mutable copy是深拷贝,可变对象的copy和mutable copy是深拷贝。