1.深拷贝和浅拷贝
- 对于非集合对象
- copy:因为copy默认返回的是不可变的,所以当我们对一个不可变的字符串进行copy的时候,我们只是拷贝了它的指针(浅拷贝)。当我们对一个可变的字符串进行拷贝的时候,因为类型转变了,我们需对其进行深拷贝。
- mutableCopy:默认返回的是一个可变的对象,适用于可变的对象,例如NSMutableString,NSMutableArray,NSMutableDictionary、etc。 无论对于可变的字符串还是不可变的字符串进行mutableCopy,系统都默认进行深拷贝,那么为什么对于相同类型的进行mutableCopy返回的仍然是新的对象呢,因为在这里系统要保证,旧的对象和新的对象都是可变的,切他们之前不会相互影响。
- 对于集合对象
- 对于不可变的集合对象,copy 是浅拷贝,mutableCopy 是单层深拷贝。
- 对于可变的集合对象,无论 copy 或者 mutableCopy 都是单层深拷贝。
/*
内存管理原则:
一次alloc 对应一次release
一次retain 对应一次 release
一次copy 对应一次release
有加就有减
总结:
如果是浅拷贝:不会生成新的对象,但是系统就会对原来的对象进行retain,
所以需要对原来的对象进行一次
如果是深拷贝:会生成新的对象,系统不会对原来的对象进行retain,但是因为生成了新的对象,所以我们需要对新的对象进行release
*/
//1.创建一个不可变字符串
NSString *str = [[NSString alloc]initWithFormat:@"lnj"];
NSLog(@"str = %lu", [str retainCount]);
/*
*/
//不会生成新的对象,但是需要注意,正是因为不会生产新的对象,所以系统会对以前的对象进行一次retain
//如果是浅拷贝,那么系统就会对原来的对象进行retain
NSString *str2 = [str copy];
NSLog(@"str = %lu", [str retainCount]);
[str release];
[str release];
//深拷贝:会生成新的对象,正是因为会生成新的对象,所以系统不会对以前的对象进行retain,但是因为生成了新的对象,所以我们需要对新的对象进行release
NSMutableString *strM = [str mutableCopy];
NSLog(@"str = %lu", [str retainCount]);
NSLog(@"strM = %lu", [strM retainCount]);
NSLog(@"%p - %p", str, strM);
[strM release];
[str release];
2.copy的第一个用途, 防止外界修改内部的数据
NSMutableString *temp = [NSMutableString stringWithFormat:@"lnj"];
Person *p = [[Person alloc] init];
p.name = temp;
// 问题: 修改了外面的变量, 影响到了对象中的属性
[temp appendString:@" cool"];
NSLog(@"name = %@", p.name);
// 记住: 以后字符串属性都用copy
3.可以使用copy保存block, 这样可以保住block中使用的外界对象的命
// block默认存储在栈中, 栈中的block访问到了外界的对象, 不会对对象进行retain
// block如果在堆中, 如果在block中访问了外界的对象, 会对外界的对象进行一次retain
Person *p = [[Person alloc] init];
NSLog(@"retainCount = %lu", [p retainCount]);
void (^myBlock)() = ^{
NSLog(@"%@", p);
NSLog(@"retainCount = %lu", [p retainCount]);
};
Block_copy(myBlock); // 将block转移到堆中
myBlock();
4.copy block之后引发循环引用
#import "ViewController.h"
typedef void (^personBlock)();
@interface Person:NSObject
@property (nonatomic,copy)NSString *name;
@property (nonatomic,copy)personBlock pBlock;
@end
@implementation Person
@end
@interface ViewController ()
@property(nonatomic,copy)NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 3.注意点: copy block之后引发循环引用
// 如果对象中的block又用到了对象自己, 那么为了避免内存泄露, 应该将对象修饰为__block
__block Person *p = [[Person alloc] init]; // 1
p.name = @"lnj";
NSLog(@"retainCount = %lu", [p retainCount]);
p.pBlock = ^{
NSLog(@"name = %@", p.name); // 2
};
p.pBlock();
NSLog(@"retainCount = %lu", [p retainCount]);
[p release]; // 1
// [p release]; // 2B
}
@end
5.自定义类实现copy
/*
1.以后想让自定义的对象能够被copy只需要遵守NSCopying协议
2.实现协议中的- (id)copyWithZone:(NSZone *)zone
3.在- (id)copyWithZone:(NSZone *)zone方法中创建一个副本对象, 然后将当前对象的值赋值给副本对象即可
*/
Person *p = [[Person alloc] init];
p.age = 30;
p.name = @"lnj";
NSLog(@"%@", p);
// Person *p2 = [p copy];
Person *p2 = [p mutableCopy];
NSLog(@"%@", p2);
- (id)copyWithZone:(NSZone *)zone
{
// 1.创建一个新的对象
Person *p = [[[self class] allocWithZone:zone] init];
// 2.设置当前对象的内容给新的对象
p.age = _age;
p.name = _name;
// 3.返回新的对象
return p;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
// 1.创建一个新的对象
Person *p = [[[self class] allocWithZone:zone] init];
// 2.设置当前对象的内容给新的对象
p.age = _age;
p.name = _name;
// 3.返回新的对象
return p;
}
// 如果想让子类在copy的时候保留子类的属性, 那么必须重写copyWithZone方法, 在该方法中先调用父类创建副本设置值, 然后再设置子类特有的值
- (id)copyWithZone:(NSZone *)zone
{
// 1.创建副本
// id obj = [[self class] allocWithZone:zone];
id obj = [super copyWithZone:zone];
// 2.设置数据给副本
// [obj setAge:[self age]];
// [obj setName:[self name]];
[obj setHeight:_height];
// 3.返回副本
return obj;
}
6.ARC与MRC混合编程
Xcode 工程中我们可以使用 ARC 和MRC 的混合模式。
选中 Target, 在 Objective C language 地方,将 ARC 设为 YES 或 NO。 这是一个总开关。 如有特别的文件, 可以单独加上开关。
如何在未使用arc的工程中引入一个使用了arc特性的文件:
打开工程找到target -> Build Phases -> Compile Sources.
双击需要改模式的 代码对应的 .m 文件
在弹出窗口中输入上面提到的标签 -fobjc-arc (ARC模式) -fno-objc-arc(MRC模式)。
回车 保存 即可