一、MRC
1. MRC基本介绍
- 现在我们
iOS
开发都在使用 ARC
,基本上不会使用 MRC
,那为什么我们还要学习 MRC
呢?
- 因为
ARC
的本质就是 MRC
,我们只有学习了 MRC
,才能理解 ARC
下的许多操作,以及解决一些诡异的问题
2. 内存泄露是什么意思?
- 无论
ARC
还是 MRC
都是内存管理
,既然是内存管理
就离不开内存泄露
的概念
-
内存泄露
:该释放的对象没有释放
3. 请在 MRC 下,实现 Person
对象有一条Dog
,调用Dog
的 run 方法
,最后 Dog
和 Person
都被释放。
#import "Person.h"
@interface Person : NSObject
{
Dog *_dog;
}
- (Dog*)dog;
- (void)setDog:(Dog*)dog;
@end
#import "Person.h"
@implementation Person
- (void)setDog:(Dog*)dog{
_dog = dog;
[_dog retain];
}
- (Dog*)dog {
return _dog;
}
- (void)dealloc
{
NSLog(@"%s", __func__);
[_dog release];
[super dealloc];
}
@end
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
Dog* dog1 = [[Dog alloc] init];
person.dog = dog1;
[dog1 release];
[person.dog run];
[person release];
}
NSLog(@"2");
return 0;
}
- 上述代码能正常调用 dog 的 run 方法吗?
- 能
- 上述代码能正常释放 dog 和 person 对象吗?
- 能
- 上述代码严谨吗?在什么情况下会报错或者内存泄露?
-
不严谨,①当给
person
重复设置值 dog1
时,dog1
将无法释放;② 当给 person
换一条狗 dog2
的时候,dog1
将无法释放;
4. 思考我们如下修改 Person.m
可以解决问题吗?
#import "Person.h"
@implementation Person
- (void)setDog:(Dog*)dog{
[_dog release];
_dog = [dog retain];
}
- (Dog*)dog {
return _dog;
}
- (void)dealloc
{
NSLog(@"%s", __func__);
[_dog release];
[super dealloc];
}
@end
- 如上写法还是不够严谨
- 如下调用,开启 Xcode 的
Zombie Objects
僵尸对象检查功能,就会报错
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
Dog* dog1 = [[Dog alloc] init]; // 1
person.dog = dog1; // 2
[dog1 release]; // 1
person.dog = dog1; // 内部 set 方法将 `dog1` 的引用计数器释放到 `0`
[person.dog run];
[person release];
}
NSLog(@"2");
return 0;
}
- 因为不够严谨,容易导致我们会将
dog1
的引用计数器释放到 0
5. 针对上一个问题,我们如何继续完善代码?
#import "Person.h"
@implementation Person
- (void)setDog:(Dog*)dog{
if (_dog != dog) {
[_dog release];
_dog = [dog retain];
}
}
- (Dog*)dog {
return _dog;
}
- (void)dealloc
{
NSLog(@"%s", __func__);
[_dog release];
[super dealloc];
}
@end
- 上述
Person
就是我们在 MRC
下常用的写法,也是我们 ARC
下编写代码会转换成的`最终样子。
6. 补充介绍:什么是 Xcode 的 Zombie Objects
僵尸对象检查功能?
- 开启方法:先选中
Product -> Scheme -> Edit Scheme -> Diagnostics -> 勾选Zombie Objects
项
- 原理:通过生成僵尸对象来替换
dealloc
的实现,当对象引用计数为0
的时候,将需要dealloc
的对象转化为僵尸对象
- 功能:如果之后再给这个僵尸对象发消息,则抛出异常
二、 copy
1. 拷贝的目的(记住这个,可以帮助我们理解很多代码层次的知识)
- 产生一个
副本对象
,跟源对象
互不影响
- 修改了
源对象
,不会影响副本对象
- 修改了
副本对象
,不会影响源对象
2. iOS 提供了 2 个拷贝的方法
-
copy
:不可变拷贝,产生不可变副本
-
mutableCopy
:可变拷贝,产生可变副本
3. 深拷贝和浅拷贝
-
深拷贝
: 内容拷贝,产生新对象
-
浅拷贝
: 指针拷贝,没有产生新的对象
4. copy
和 mutableCopy
对NSArray、NSMutableArray、NSString、NSMutableString、NSDictionary、NSMutableDictionary
的效果有什么不同?

`copy` 和 `mutableCopy` 效果图
5.思考下面这句代码写法有问题吗?
@property(copy, nonatomic) NSMutableArray *data;
- 这是一种不好的写法
-
data
如果用 copy
修饰,那么 data
的类型实际上就是 NSArray
。然而又告诉别人这个是 NSMutableArray
类型,如果别人调用 NSMutableArray
特有的方法,就会报错。
6.为什么字符串普遍都用 copy 修饰呢?

UITextField 中字符串的修饰
- 这样我们可以
保证内部
拿到的字符串
不轻易受外部的影响
7. 对于 Foundation 框架内部有许多类为我们实现了 copy
和 mutableCopy
,那如果对于我们自定义的类,比如 Person
调用 copy
会有效果吗?
- 不能对自定义的类之间调用
copy
- 会报错
-[Person copyWithZone:]: unrecognized selector sent to instance 0x100494530
- 需要遵守
NSCopying
协议,以及实现 -[Person copyWithZone:]
方法
- (id)copyWithZone:(NSZone *)zone {
Person *person = [[Person allocWithZone:zone] init];
person.age = self.age;
person.weight = self.weight;
return person;
}
网友评论