「iOS」————MRC

底层学习


MRC

MRC :手动引用计数管理,通过调用 retainreleaseautorelease 等方法来控制对象的生命周期。

对象操作 OC方法
生成并且持有对象 alloc/new/copy/mutableCopy等方法
持有对象 retain方法
释放对象 release
废弃对象 dealloc

对于内存管理考虑方式有四点:

单个对象:

  • 自己生成的对象,自己持有

  • 非自己生成的对象,自己也可以持有

    objectivec 复制代码
    NSMutableArray* ary = [NSMutableArray array];
    [ary reatin];
  • 不再需要自己持有的对象时释放

  • 非自己持有的对象无法释放

多个对象内存管理思想

  • 一个对象不持有另外一个对象

此时两个实例对象没有关联可以将各自释放掉

  • 一个对象持有另外一个对象

持有的对象需要在set方法中引用计数加一。释放时也需要将持有的对象销毁

objectivec 复制代码
- (void)setMyObject:(MyObject*)myObj { 
    // 引用计数器 +1
    [myObj retain];
    _myObj = myObj;
}

- (void)dealloc {
    // 人释放了, 那么房间也需要释放
    [_myObj release];
    NSLog(@"%s", __func__);
 
    [super dealloc];
}
  • 一个对象持有并释放掉一个对象后持有另外一个对象

此时则需要在更换原持有的对象前,释放第一个对象

objectivec 复制代码
- (void)setMyObject:(MyObject*)myObj { 
		[_myObj release];
    // 引用计数器 +1
    [myObj retain];
    _myObj = myObj;
}
  • 一个对象持有一个对象并释放该对象后再次持有该对象

此种情况使用上一个getter方法不难发现,我们将那个对象赋值给该对象后该对象对那个对象的引用计数加一,再将那个对象引用计数减一,此时两个对象的引用计数相同,此时再次进行赋值会导致那个对象的引用计数先减一此时为0变成了野指针,此时再对野指针进行retain操作就会报错。

此时就需要在setter方法中判断是否重复赋值,如果是同一个实例对象,就不需要重复进行 release 和 retain。

objectivec 复制代码
- (void)setMyObject:(MyObject*)myObj { 
   if (_myObj != myObj) {
   [_myObj release];
    // 引用计数器 +1
    [myObj retain];
    _myObj = myObj;
   }
}

@property参数

  • 在成员变量前加上 @property,系统就会自动帮我们生成基本的 setter / getter 方法,但是不会生成内存管理相关的代码。
objectivec 复制代码
@property (nonatomic) int val;
  • 如果在 property 后边加上 assign,系统也不会帮我们生成 setter 方法内存管理的代码,仅仅只会生成普通的 getter / setter 方法,默认什么都不写就是 assign
objectivec 复制代码
@property(nonatomic, assign) int val;
  • 如果在 property 后边加上 retain,系统就会自动帮我们生成 getter / setter 方法内存管理的代码,但是仍需要我们自己重写 dealloc 方法。
objectivec 复制代码
@property(nonatomic, retain)MyObject *myObj;

自动释放池

autorelease 是一种支持引用计数的内存管理方式,主要用于延迟对象的释放,只要给对象发送一条 autorelease 消息,会将对象注册到一个自动释放池中,当自动释放池被销毁时,会对池子里面的所有对象做一次 release 操作。

autorelease方法会返回对象本身,且调用完autorelease方法后,对象的计数器不变。

objectivec 复制代码
Person *p = [Person new];
p = [p autorelease];
NSLog(@"count = %lu", [p retainCount]); // 计数还为 1

创建

  • 第一种是使用 NSAutoreleasePool 创建
objectivec 复制代码
NSAutoreleasePoo *pool = [[NSAutoreleasePool alloc] init]; //创建自动释放池

[pool release];  [pool drain]; //销毁自动释放池
  • 第二种是使用 @autoreleasepool 创建
objectivec 复制代码
@autoreleasepool
{ // 开始代表创建自动释放池
 
} // 结束代表销毁自动释放池

使用

需要注意的是对象调用autorelease方法需要在创建autoreleasepool之后否则无法将其注册到autoreleasepool。

objectivec 复制代码
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
Person *p = [[[Person alloc] init] autorelease];
[autoreleasePool drain];

@autoreleasepool
{ // 创建一个自动释放池
        Person *p = [[Person new] autorelease];
        // 将代码写到这里就放入了自动释放池
} // 销毁自动释放池(会给池子中所有对象发送一条 release 消息)

//以上两种使用方法相同

自动释放池的嵌套

  • 自动释放池是以栈的形式存在。
  • 由于栈只有一个入口,所以调用 autorelease 会将对象放到栈顶的自动释放池。
objectivec 复制代码
@autoreleasepool { // 栈底自动释放池
    @autoreleasepool {
        @autoreleasepool { // 栈顶自动释放池
            Person *p = [[[Person alloc] init] autorelease];
        }
        Person *p = [[[Person alloc] init] autorelease];
    }
}

注意事项

  • 自动释放池中不适宜放占用内存比较大的对象

因为自动释放池是延迟释放机制,只有清空池子时才会将注册到池子的对象销毁,此时如果有多个内存较大的对象就会造成短时间内内存暴涨。

  • 不要把大量循环操作放到同一个 @autoreleasepool 之间,这样会造成内存峰值的上升。如果一定要循环,则这样使用

    objectivec 复制代码
    // 内存暴涨
    @autoreleasepool {
        for (int i = 0; i < 99999; ++i) {
            Person *p = [[[Person alloc] init] autorelease];
        }
    }
    // 内存不会暴涨,在循环内部创建自动释放池
    for (int i = 0; i < 99999; ++i) {
        @autoreleasepool {
            Person *p = [[[Person alloc] init] autorelease];
        }
    }
  • 不要连续调用 autorelease ,也不要调用后又调用release

objectivec 复制代码
@autoreleassepool {
 // 会导致过度释放
    Person *p = [[[[Person alloc] init] autorelease] autorelease];
}

@autoreleasepool {
    Person *p = [[[Person alloc] init] autorelease];
    [p release]; //过度释放
}

循环引用

两个对象互相持有对方,A对象的销毁依赖于B,B对象的销毁依赖于A

1.自循环引用

假如有一个对象,内部强持有它的成员变量obj,若此时我们给obj赋值为原对象时,就是自循环引用。

2.相互循环引用

对象A内部强持有obj,对象B内部强持有obj,若此时对象A的obj指向对象B,同时对象B中的obj指向对象A,就是相互引用。

3.多循环引用

假如类中有对象1...对象N,每个对象中都强持有一个obj,若每个对象的obj都指向下个对象,就产生了多循环引用。

相关推荐
和沐阳学逆向3 小时前
iOS逆向_古法逆向_Instagram最新版抓包
macos·ios·cocoa
自学AI的鲨鱼儿8 小时前
mac npm 安装 codex 报错 npm ENOTEMPTY
macos·npm·codex
Digitally11 小时前
如何将真我(realme)手机数据传输至 iPhone
ios·智能手机·iphone
Sephiroth.Ma15 小时前
Mac 提示“Docker 已损坏,无法打开”?我这样排查后 10 分钟修好
macos·docker·容器
量子炒饭大师15 小时前
【OpenClaw修炼宝典】—— 【macOS安装篇】想玩《爪子船长》复刻版却卡在安装?OpenClaw 从零环境搭建与编译全攻略 (小白避坑指南)
macos·openclaw·小龙虾·龙虾
JFSJHFZJ15 小时前
解密iPhone核心技术,读懂苹果的硬实力
ios·cocoa·iphone
不才小强16 小时前
macOS 屏幕录制开发完全指南:ScreenCaptureKit与音频采集实战
macos·音视频
JXSJHF17 小时前
iPhone隐藏功能大盘点,免费好用不占内存
ios·iphone
ShiLuoHeroKing1 天前
Mole:面向专业用户的Mac系统清理开源方案
macos