集合对象的内存管理
一、集合对象的内存管理代码分析
(1)分析1:
-分析得出结论:
1. 当对象被添加到数组中的时候,对象的引用计数器加1
2. 当数组被销毁的时候,数组会向它其中的元素发送一条release消息
(2)分析2:
-分析得出结论:
1. 当对象从数组中移除的时候,对象的引用计数器减1
二、集合内存管理总结
(1)官方内存管理原则:
1. 当调用alloc、new、copy和mutableCopy方法产生一个新对象的时候,就必须在最后调用一次release或者autorelease方法
2. 当调用retain方法让对象的引用计数器加1,就必须在最后调用一次release或者autorelease方法
(2)集合的内存管理细节:
1. 把一个对象添加到集合中的时候,这个对象会做一次retain操作,对象的计数器会加1
2. 当一个集合被销毁的时候,这个集合里面的所有的对象都会做一次release操作,计数器会减1
3. 当一个对象从集合中移除的时候,这个对象会做一次release操作,计数器会减1
(3)普遍规律:
1. 如果方法名是add \ insert开头,那么被添加的对象的计数器会加1
2. 如果方法名是remove \ delete开头,那么被删除的对象的计数器会减1
(4)特别注意:当一个集合被销毁时,它里面存储的对象不一定被销毁,仅仅是做一次release操作
copy的介绍与使用
一、copy概念及入门
(1)什么是copy
copy的字面意思是“复制”、“拷贝”,是一个产生副本的过程,常见的复制是一个源文件通过复制,产生一个副本文件
(2)对象copy的概念
对象拷贝的目的:要使用某个对象的数据,但是在修改对象的时候不影响原来对象的内容
二、copy的特点
1. 修改源文件的内容,不会影响副本文件;修改副本文件的内容,不会影响源文件
2. OC中的copy,就指的是对象的拷贝,作用是利用一个源对象产生一个副本对象
3. 修改源对象的属性和行为,不会影响副本对象;修改副本对象的属性和行为,不会影响源对象
三、如何使用copy的功能
一个对象可以调用copy方法或mutableCopy方法来产生一个副本对象
1. copy:创建的是不可变副本(如:NSString、NSArray、NSDictionary)
2. mutableCopy:创建的是可变副本(如:NSMutableString、NSMutableArray、NSMutableDictionary)
四、使用copy功能的前提
1. copy:需要遵守NSCopying协议,实现copyWithZone:方法
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
- mutableCopy:需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
五、copy和mutableCopy功能的代码分析
分析得出结论:
1. copy:创建的是不可变副本
2. mutableCopy:创建的是可变副本
六、深复制和浅复制
1. copy就是浅复制,因为没有产生新的对象
2. mutableCopy就是深复制,会在内存中重新分配一块内存空间
3. 有没有产生新的对象也是判断深浅复制的标准
七、copy与内存管理的代码分析
(1)分析一:
分析得出结论:
1. 由于地址相同,所以可以得出结论:不可变的对象进行copy以后,没有重新分配内存空间
2. 这是浅拷贝,只拷贝了地址,并没有真正的分配新的内存空间(源对象的引用计数器加1,副本对象指向源对象,引用计数器也增加了)
(2)分析二:
分析得出结论:
1. 由于地址不相同,所以可以得出结论:不可变的对象进行mutableCopy以后,重新分配一个新内存空间
2. 这是深拷贝,开辟了一个新的空间存储副本对象(副本对象的引用计数器为1)
八、copy与内存管理的总结:
(1)深复制(深拷贝、内容复制、deep copy)
1. 源对象和副本对象是不同的两个对象
2. 源对象的引用计数器不变,副本对象的引用计数器为1
3. 本质:产生了新的对象
(2)浅复制(浅拷贝、指针拷贝、shallow copy)
1. 源对象和副本对象是同一个对象
2. 源对象(副本对象)引用计数器加1,相当于做了一次retain操作
3. 本质:没有产生新的对象
九、@property中的copy关键字
@property(nonatomic, copy)NSString *name;
// set方法的展开形式
- (void)setName:(NSString *)name
{
if (name != _name)
{
[_name release];
_name = [name copy];
}
}
十、@property内存管理策略选择
(1)非ARC(MRC)
1. copy:只用于NSString \ block
2. retain:除NSString \ block以外的OC对象
3. assign:基本数据类型,枚举、结构体(非OC对象),当两个对象循环引用的时候,一端用retain,另一端使用assign
(2)ARC
1. copy:只用于NSString \ block
2. strong:除NSString \ block以外的OC对象
3. weak:当两个对象循环引用的时候,一端用strong,另一端使用weak
4. assign:基本数据类型,枚举、结构体(非OC对象)
十一、为自定义类实现copy操作
(1)自定义对象copy步骤
1. 新建Person类
2. 为Person类实现copy操作
<1> 让Person类遵守NSCopying协议
<2> 实现copyWithZone:方法,在该法中返回一个对象的副本即可
<3> 在copyWithZone方法中,创建一个新的对象,并设置该对象的数据与现有对象一致,并返回该对象
3. 创建Person对象,调用copy方法,查看地址
(2)细节介绍
1. 调用copy其实就是调用copyWithZone方法,所以实现copyWithZone方法(查看NSObject协议中的copy方法介绍)
2. copyWithZone方法返回值类型是id类型,需要返回一个对象的副本
3. 关于copyWithZone的参数zone问题
<1> zone:表示空间,分配对象是需要内存的,如果指定了zone,就可以指定新建对象对应的内存空间,但是:zone是一个非常古老的技术,为了避免在堆中出现内存碎片而使用的,在今天的开发中,zone几乎可以忽略
<2>查看NSObject协议中的copyWithZone:方法介绍(zone参数可以被忽略,是历史原因)
4. 如果对象没有可变 / 不可变的版本区别,只要实现copyWithZone方法即可
十二、为自定义类实现copy操作的代码实现
(1)Person类的设计
Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCopying>
@property(nonatomic, assign)int age; // 年龄
@property(nonatomic, assign)float weight; // 体重
@end
Person.m文件
@implementation Person
// 实现copyWithZone:(NSZone *)zone方法
- (id)copyWithZone:(NSZone *)zone
{
Person *p = [[Person alloc] init];
p.weight = self.weight;
p.age = self.age;
return p;
}
@end
(2)main.m文件
注意:
1. 自定义对象的拷贝都是深拷贝
十三、为Person类实现mutableCopy操作
1. 遵守NSMutableCopying协议
2. 实现协议中需要实现的方法
// 实现mutableCopyWithZone:(NSZone *)zone
- (id)mutableCopyWithZone:(NSZone *)zone
{
Person *p = [[Person alloc] init];
p.weight = self.weight;
p.age = self.age;
return p;
}
十四、为Person类实现mutableCopy操作的代码实现
(1)Person类的设计
Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSMutableCopying>
@property(nonatomic, assign)int age; // 年龄
@property(nonatomic, assign)float weight; // 体重
@end
Person.m文件
#import "Person.h"
@implementation Person
// 实现mutableCopyWithZone:(NSZone *)zone
- (id)mutableCopyWithZone:(NSZone *)zone
{
Person *p = [[Person alloc] init];
p.weight = self.weight;
p.age = self.age;
return p;
}
@end
(2)main.m文件