①语言Swift

①语言Swift

[Toc]

一、Copy

  1. 如何让自己的类用copy修饰符

    若想令自己所写的对象具有拷贝功能,则需实现NSCopying协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopyingNSMutableCopying协议。
    具体步骤:
    1.需声明该类遵从NSCopying协议
    2.实现NSCopying协议的方法,具体区别戳这里

  • NSCopying协议方法为:
1
2
3
4
5
- (id)copyWithZone:(NSZone *)zone {
MyObject *copy = [[[self class] allocWithZone: zone] init];
copy.username = self.username;
return copy;
}

1、浅拷贝和深拷贝的区别

浅层复制:只复制指向对象的指针,而不复制引用对象本身。
深层复制:复制引用对象本身。

意思就是说我有个A对象,复制一份后得到A_copy对象后,

对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只不过是是一个指针,对象本身资源还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背了我们复制拷贝的一个思想。
而对于深复制就好理解了,内存中存在了两份独立对象本身。

通俗的话将就是:
浅复制好比你和你的影子,你完蛋,你的影子也完蛋;
深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。

2、 iOS - Copy 与 MutableCopy

iOS - Copy 与 MutableCopy
①copy:因为copy默认返回的是不可变的,所以当我们对一个不可变的字符串进行copy的时候,我们只是拷贝了它的指针(浅拷贝)。当我们对一个可变的字符串进行拷贝的时候,因为类型转变了,我们需对其进行深拷贝。
mutableCopy:默认返回的是一个可变的对象,适用于可变的对象,例如NSMutableString,NSMutableArray,NSMutableDictionary、etc。 无论对于可变的字符串还是不可变的字符串进行mutableCopy,系统都默认进行深拷贝。

其他参考

(1)、copy到底是深拷贝还是浅拷贝?

当我们对一个不可变对象(NSString类型)使用copy关键字的时候,系统是不会产生一个新对象,因为原来的对象是不能修改的,拷贝出来的对象也是不能修改的,那么既然两个都不可以修改,所以这两个对象永远也不会影响到另一个对象(符合我们说的“修改新(旧)对象,不影响旧(新)对象”原则),系统为了节省内存,所以就不会产生一个新的对象了。那么问题来了,copy到底是深拷贝还是浅拷贝?答:是否是深浅拷贝,是否创建新的对象,是由程序运行的环境所造成的,并不是一概而论。

(2)、这个写法会出什么问题@property (nonatomic, copy) NSMutableArray *mutableArray;

添加,删除,修改数组内元素的时候,程序会因为找不到对应的方法而崩溃。原因:

self.mutableArray = xxx;在copy的修饰下执行的是 self.mutableArray = [xxx copy];进行了浅拷贝,得到的是一个xxx的副本,且该副本是一个不可变的数组。导致在运行的时候,其实你的mutableArray已经是NSArray类了。从而在添加,删除,修改数组内元素的时候,程序会因为找不到对应的方法而崩溃。

3、为什么NSArray用copy修饰、NSMutableArray用strong修饰

把NSMutableArray用copy修饰有时就会crash,因为copy后的数组变成了不可变数组NSArray.当你对不可变数组NSArray进行增删改操作的时候就会crash,
举例如下:

1
2
3
4
5
6
7
①NSMutableArray用copy属性造成的crash:
@property (nonatomic, copy) NSMutableArray *mutableArray1; // 会崩溃
@property (nonatomic, strong) NSMutableArray *mutableArray2; // 正确

NSMutableArray *array = [NSMutableArray arrayWithArray:@[[Model1 new], [Model1 new]]];
self.mutableArray1 = array; // copy: mutableArray1是array的副本,且通过打印验证是浅拷贝。且此时aArray的值mArray的一个副本,该副本是通过[mArray copy]进行的浅拷贝得到的一个不可变新对象,即类型在执行时候变为了 NSArray了。所以,如果对归属为NSArray了的aArray执行NSMutableArray才有的如removeAllObjects的方法时,就会崩溃。
self.mutableArray2 = array; // strong:mutableArray1是array自身

当修饰可变类型的属性时,如NSMutableArray、NSMutableDictionary、NSMutableString,用strong。

当修饰不可变类型的属性时,如NSArray、NSDictionary、NSString,用copy。

4、模型数组深拷贝

通常需要实现对模型的拷贝都需要先实现NSCopying、 NSMutableCopying协议。注意:如果是数组使用拷贝操作是不会对数组内实现copy协议的对象进行深拷贝的。

参考文章:iOS 模型数组深拷贝

1、最笨的方法就是通过遍历逐个拷贝元素

1
2
3
4
NSMutableArray *array = [NSMutableArray array];
for (Person *person in dataSourceAry) {
[array addObject:[person copy]];
}

2、也有人使用归档解档实现数组内部元素拷贝

3、这么好用的一个方法现在才发现(推荐)

1
- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag
1
2
3
4
5
6
7
8
NSArray <Person *>*deepCopyAry = [[NSArray alloc]initWithArray:dataSourceAry copyItems:YES];
NSLog(@"<dataSourceAry: %@>", dataSourceAry);
NSLog(@"<deepCopyAry: %@>", deepCopyAry);

[deepCopyAry enumerateObjectsUsingBlock:^(Person *obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj.name = @"弗兰克";
obj.dog.name = @"弗兰克的dog";
}];

4、分析NSString、NSMutableString等类的copy、mutableCopy

在语言文章中,我们已经说明对于语句NSString *obj = [[NSData alloc] init]; obj在编译时是NSString的类型;运行时是NSData类型的对象。

由此得出的结论是,不要被编译时的类型蒙蔽,还要看实际运行时的类型。

(1)、分别对NSString、NSMutableString进行copy、mutableCopy生成的类型是什么?

你可以片面理解为

copy是[NSString alloc],所以生成的都是不可变的;
mutableCopy是[NSMutableString alloc],所以生成的都是可变的;

所以以下代码的结果,即为代码中的注释一样

1
2
3
4
5
6
7
8
9
10
>NSString *str1 = @"test001";
>
>NSMutableString *str2 = [str1 copy];
>//编译时,str2是NSMutableString类型。因为是把str2声明为可变字符串,所以str2即为声明的可变字符串
>//运行时,str2是NSString类型。因为是copy,所以不管str1是可变不可变,str2都是不可变字符串
>
>NSMutableString *str3 = [str1 mutableCopy];
>//编译时,str3是NSMutableString类型。因为是把str3声明为可变字符串,所以str3即为声明的可变字符串
>//运行时,str3是NSMutableString类型。因为是mutableCopy,所以不管str1是可变不可变,str3都是可变字符串
>
(2)、分别对NSString、NSMutableString进行copy、mutableCopy操作,是否会开辟新地址,即是属于深拷贝还是浅拷贝?

copy:对[string copy]是浅拷贝,即不会开辟新地址,而对[mutableString copy]是深拷贝,会开辟新地址。

mutableCopy:不管是对[string mutableCopy],还是对[mutableString mutableCopy],都是深拷贝,都会开辟新地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)testCopy {
NSString *testStr = [NSString stringWithFormat:@"123"];
id copyedStr = [testStr copy];
id mutableCopyStr = [testStr mutableCopy];
NSLog(@"=== 对NSString变量进行copy、mutableCopy得到的地址和类型分别是 ===");
NSLog(@"testStr = %p, class: %@", testStr, NSStringFromClass([testStr class]));
NSLog(@"copyedStr = %p, class: %@", copyedStr, NSStringFromClass([copyedStr class]));
NSLog(@"mutableCopyStr = %p, class: %@", mutableCopyStr, NSStringFromClass([mutableCopyStr class]));

NSLog(@"\n");

NSMutableString *testMutableStr = [NSMutableString stringWithFormat:@"mutable_456"];
id copyedMutableStr = [testMutableStr copy];
id mutableCopyMutableStr = [testMutableStr mutableCopy];
NSLog(@"=== 对NSMutableString变量进行copy、mutableCopy得到的地址和类型分别是 ===");
NSLog(@"testMutableStr = %p, class: %@", testMutableStr, NSStringFromClass([testMutableStr class]));
NSLog(@"copyedMutableStr = %p, class: %@", copyedMutableStr, NSStringFromClass([copyedMutableStr class]));
NSLog(@"mutableCopyMutableStr = %p, class: %@", mutableCopyMutableStr, NSStringFromClass([mutableCopyMutableStr class]));
}

对NSString、NSMutableString变量进行copy、mutableCopy得到的地址和类型分别是

结论:分别对NSString、NSMutableString进行copy、mutableCopy操作,只有NSString copy是浅拷贝

(3)、将NSString、NSMutableString变量赋值给用copy、strong修饰NSString属性的时候,是否会开辟新地址,即是属于深拷贝还是浅拷贝?(附:NSString用copy修饰是为什么)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@property (nonatomic, copy) NSString *copyedStr;
@property (nonatomic, strong) NSString *strongStr;

@property (nonatomic, copy) NSString *copyedMutableStr;
@property (nonatomic, strong) NSString *strongMutableStr;

///将NSString、NSMutableString变量赋值给用copy、strong修饰的NSString属性的时候
- (void)testCopy {
NSString *testStr = [NSString stringWithFormat:@"123"];
self.copyedStr = testStr;
self.strongStr = testStr;
NSLog(@"=== 将NSString变量赋值给用copy、strong修饰的NSString属性的时候 ===");
NSLog(@"testStr = %p", testStr);
NSLog(@"copyedStr = %p", self.copyedStr);
NSLog(@"strongStr = %p", self.strongStr);

NSLog(@"\n");

NSMutableString *testMutableStr = [NSMutableString stringWithFormat:@"mutable_456"];
self.copyedMutableStr = testMutableStr;
self.strongMutableStr = testMutableStr;
NSLog(@"=== 将NSMutableString变量赋值给用copy、strong修饰的NSString属性的时候 ===");
NSLog(@"testMutableStr = %p", testMutableStr);
NSLog(@"copyedMutableStr = %p", self.copyedMutableStr);
NSLog(@"strongMutableStr = %p", self.strongMutableStr);
}

将NSString、NSMutableString赋值给用copy、strong修饰的NSString变量

可以看出,

①将NSString变量赋值给用copy、strong修饰的NSString属性的时候,不管是strong还是copy属性的对象,其指向的地址都是同一个,即为string指向的地址。

1
2
3
4
5
6
@property (nonatomic, copy) NSString *copyedStr;
@property (nonatomic, strong) NSString *strongStr;

NSString *testStr = [NSString stringWithFormat:@"123"];
self.copyedStr = testStr;
self.strongStr = testStr;

②将NSMutableString变量赋值给用copy、strong修饰的NSString属性的时候,

此时copy属性字符串copyedMutableStr

1
2
3
4
@property (nonatomic, copy) NSString *copyedMutableStr;

NSMutableString *testMutableStr = [NSMutableString stringWithFormat:@"mutable_456"];
self.copyedMutableStr = testMutableStr;

深拷贝了testMutableStr字符串,并让copyedMutableStr对象指向这个字符串(即copyedMutableStr和testMutableStr只是对象值一样,但不是同一个了)。所以此时,我们如果去修改testMutableStr字符串的话,可以看到,我们用@property (nonatomic, copy) NSString *copyedMutableStr;修饰的copyedMutableStr能够不会因为其赋值源testMutableStr的改变而改变,也就保证了安全性。

而strong属性字符串strongMutableStr

1
2
3
4
@property (nonatomic, strong) NSString *strongMutableStr;

NSMutableString *testMutableStr = [NSMutableString stringWithFormat:@"mutable_456"];
self.strongMutableStr = testMutableStr;

因为strongMutableStr与testMutableStr是指向同一对象,所以strongMutableStr的值也会跟随着改变;

综上:所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变(包括不希望赋值后,其他原来的值的改变会改变到它),所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。使用copy来修饰无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本,这样更安全。

(4)、将NSString、NSMutableString变量赋值给用copy、strong修饰的NSMutableString属性的时候,是否会开辟新地址,即是属于深拷贝还是浅拷贝?
1
2
3
4
5
6
7
8
9
10
11
12
13
@property (nonatomic, copy) NSMutableString *copyedStr;
@property (nonatomic, strong) NSMutableString *strongStr;

NSString *testStr = [NSString stringWithFormat:@"123"];
self.copyedStr = testStr;
self.strongStr = testStr;



@property (nonatomic, copy) NSMutableString *copyedMutableStr;

NSMutableString *testMutableStr = [NSMutableString stringWithFormat:@"mutable_456"];
self.copyedMutableStr = testMutableStr;

2.自定义类如何让它具有copy功能?
遵守NScoping协议,实现copywithzone方法即可.

(5)、自己代码实现copy修饰符,应该怎么写????

4、NSCoding和NSCopy

NSCoding和NSCopy

(1)、NSCoding的作用

iOS通过NSCoding保存实体对象

很多时候我们都需要将对象序列化,比如将一个对象存入到NSUserDefault 里面去的时候,由于NSUserDefault支持存入的类型有限制,所以很多时候我们需要将NSObject类型的对象转换成NSData再存入进去。

(2)、NSCopy

当你要进行对象拷贝的时候需要遵循NSCopy协议

1
2
3
4
5
6
7
8
9
- (id)copyWithZone:(NSZone *)zone {
id copy = [[[self class] alloc] init];
if (copy) {
[copy setId:[self.id copyWithZone:zone]];
[copy setNickName:[self.nickName copyWithZone:zone]];
}

return copy;
}

5、@synthesize和@dynamic区别

@synthesize和@dynamic区别

在声明property属性后,有2种实现选择

  • @synthesize

编译器期间,让编译器自动生成getter/setter方法。当有自定义的存或取方法时,自定义会屏蔽自动生成该方法

  • @dynamic

告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告,然后由自己实现存取方法,或存取方法在运行时动态创建绑定:主要使用在CoreData的实现NSManagedObject子类时使用,由Core Data框架在程序运行的时动态生成子类属性