iOS|iOS 底层 day25 内存管理 MRC copy
一、MRC
1. MRC基本介绍
- 现在我们
iOS
开发都在使用ARC
,基本上不会使用MRC
,那为什么我们还要学习MRC
呢? - 因为
ARC
的本质就是MRC
,我们只有学习了MRC
,才能理解ARC
下的许多操作,以及解决一些诡异的问题
- 无论
ARC
还是MRC
都是内存管理
,既然是内存管理
就离不开内存泄露
的概念 -
内存泄露
:该释放的对象没有释放
Person
对象有一条Dog
,调用Dog
的 run 方法
,最后 Dog
和 Person
都被释放。
-
Dog.h
代码如下
#import "Person.h"
@interface Person : NSObject
{
Dog *_dog;
}
- (Dog*)dog;
- (void)setDog:(Dog*)dog;
@end
-
Person.m
代码如下
#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
将无法释放;
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
- 修改
Person.m
代码中的set
方法
#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
下编写代码会转换成的`最终样子。
Zombie Objects
僵尸对象检查功能?
- 开启方法:先选中
Product -> Scheme -> Edit Scheme -> Diagnostics -> 勾选Zombie Objects
项 - 原理:通过生成僵尸对象来替换
dealloc
的实现,当对象引用计数为0
的时候,将需要dealloc
的对象转化为僵尸对象
- 功能:如果之后再给这个僵尸对象发消息,则抛出异常
1. 拷贝的目的(记住这个,可以帮助我们理解很多代码层次的知识)
- 产生一个
副本对象
,跟源对象
互不影响 - 修改了
源对象
,不会影响副本对象
- 修改了
副本对象
,不会影响源对象
-
copy
:不可变拷贝,产生不可变副本 -
mutableCopy
:可变拷贝,产生可变副本
-
深拷贝
: 内容拷贝,产生新对象 -
浅拷贝
: 指针拷贝,没有产生新的对象
copy
和 mutableCopy
对NSArray、NSMutableArray、NSString、NSMutableString、NSDictionary、NSMutableDictionary
的效果有什么不同? `copy` 和 `mutableCopy` 效果图 5.思考下面这句代码写法有问题吗?
@property(copy, nonatomic) NSMutableArray *data;
- 这是一种不好的写法
-
data
如果用copy
修饰,那么data
的类型实际上就是NSArray
。然而又告诉别人这个是NSMutableArray
类型,如果别人调用NSMutableArray
特有的方法,就会报错。
- 这样我们可以
保证内部
拿到的字符串
不轻易受外部的影响
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;
}
推荐阅读
- 2020-04-07vue中Axios的封装和API接口的管理
- iOS中的Block
- 2019-01-17-晨读7期-直子Day25
- 记录iOS生成分享图片的一些问题,根据UIView生成固定尺寸的分享图片
- 2019-08-29|2019-08-29 iOS13适配那点事
- Hacking|Hacking with iOS: SwiftUI Edition - SnowSeeker 项目(一)
- 医生随笔(232)不要轻易得罪底层人
- iOS面试题--基础
- 接口|axios接口报错-参数类型错误解决
- iOS|iOS 笔记之_时间戳 + DES 加密