Objective- C学习: 手动内存管理

文章目录

对象的生命周期由 **引用计数(retain count)**决定

当对象的引用计数大于 0 的时候, 该对象可以存活, 当引用计数等于 0 的时候, 对象就需要被销毁

影响引用计数的操作

操作 结果
alloc/new/copy +1
retain +1
release -1
autorelease 延迟 -1
= 0 调用 dealloc,释放内存

retainCount: 查看计数

当对象的引用计数为 0 的时候, 程序会自动调用该对象的 dealloc 方法来销毁它

核心法则:

  1. 谁alloc/new/copy/mutableCopy -> 谁 release
  2. 谁 retain -> 谁 release
  3. 不是你创建的, 不是你 retain 的 -> 不要 release

准确来说, OC 内存管理的真正核心是一套所有权(Ownership)契约, 谁持有对象, 谁就有责任释放它, 引用计数只是这套契约的实现手段

持有的定义

代码让对象的引用计数 + 1, 并且有义务在不需要的时候让它 - 1, 不是所有拿到对象的指针的地方都持有它

一个对象可以被多个持有者同时持有, 只有所有持有者都放弃后才能真正销毁

持有的方式

通过创建持有 (alloc, new, copy, mutablecopy)

通过这四种方式创建的对象,返回的对象由你持有, 必须负责 release

objc 复制代码
NSObject* a = [[NSobject alloc] init]; 
NSObject* b =  [NSobject new]; 
NSString* c = [somestr copy]; 
NSMutableString* d = [someStr mutableCopy]; 

用完必须 release

objective-c 复制代码
[a release];  
[b release]; 
[c release]; 
[d release]; 

通过retain 主动持有

拿到了别人的对象, 想让它活的久一点 ,就需要 retain

objc 复制代码
- (void) setChild: (Person* )child {
    [_child release]; 	// 先舍弃旧的对象 
    _chile = [child retain]; // 持有新对象
}

不持有:通过非创建方法拿到的对象

方法名不以 alloc/new/copy/mutableCopy 开头,返回的是 autorelease 对象,你不持有它,不能 release

objc

objc 复制代码
// 以下对象你不持有,不要 release!
NSString *s = [NSString stringWithFormat:@"hello %d", 42];
NSArray  *a = [NSArray arrayWithObjects:@"x", @"y", nil];
NSDate   *d = [NSDate date];

// 这里直接用,没有问题
NSLog(@"%@ %@ %@", s, a, d);

// 不要这样做:
// [s release];  // 错误!没有持有它,多余的 release 导致崩溃

这些对象被放进了 autorelease pool,会在 pool drain 时自动释放。

通过 autorelease 转移所有权

autorelease 的语义是:"我现在放弃所有权,但不立刻 -1,等 pool drain 时再 -1"。常用于从方法返回对象时:

objc

objc 复制代码
// 工厂方法的标准写法
+ (instancetype)personWithName:(NSString *)name {
    Person *p = [[Person alloc] initWithName:name];
    return [p autorelease];
    // alloc 让 count=1,autorelease 把释放权交给 pool
    // 调用者收到的是一个"不归你管"的对象
}

属性关键字与引用计数的关系

strong

赋值时引用计数 +1

持有者销毁或属性被重新赋值时 -1

对应 MRC 下的 retain

weak

赋值时引用计数 不变

不持有对象,当对象被销毁时,weak 指针自动置 nil

常用于解决循环引用(delegate、block 中的 self)

assign

  • 引用计数 不变
  • 纯粹的指针赋值,对象销毁后不会自动置 nil(野指针风险)
  • 一般只用于基本数据类型(int、float、BOOL 等)

assign 只用于基本数据类型

当一个属性被 assign 修饰时,它仅仅是简单地记录了内存地址。

  • 释放时不置空 :当该对象因为引用计数归零被销毁时,assign 修饰的指针依然指向那块已经被回收的内存区域造成野指针
objc 复制代码
// delegate 对象销毁后:
_delegate = someObj;  // assign,直接指针赋值,不 retain
// someObj 在外部被 release,销毁
[_delegate doSomething];  // 崩溃!_delegate 是野指针

dealloc 完整写法

objc 复制代码
- (void)dealloc {
    // retain/copy 属性:release 并置 nil
    [_name release],      _name = nil;
    [_title release],     _title = nil;
    [_children release],  _children = nil;

    // assign 属性(对象):只置 nil,不 release(你没持有)
    _delegate = nil;

    // assign 属性(基本类型):不需要任何操作
    // _count、_flag 等不用管

    [super dealloc];  // 必须最后调用,释放父类持有的成员
}

copy

赋值时对原对象调用 copy,创建一个新对象 ,新对象引用计数为 1

原对象引用计数不变

常用于 NSString、Block,防止外部修改影响内部状态

objc 复制代码
- (void)setName:(NSString *)name {
    if (_name != name) {
        [_name release];
        _name = [name copy];  // copy 出一个新对象,引用计数=1
    }
}

retain(MRC)

  • 赋值时引用计数 +1
  • 即 ARC 下 strong 的前身

retain ---- setter持有新值, 释放旧值

objc 复制代码
- (void)setName:(NSString *)name {
    if (_name != name) {       // 必须判断自赋值
        [_name release];       // 如果不判断,self.name == _name 时:
        _name = [name retain]; // 先 release 让 count=0,对象被销毁
    }                          // 再 retain 访问已销毁对象 → 崩溃
}

在上面的 setter 方法中,先判断 _item 与被传入的 FKItem 对象是否相等也是很有必要的。如果不进行这个判断,当程序多次将同一个 FKItem 对象传给该 setter 方法时,将会导致每执行一次 setter 方法,被传入的 FKItem 对象的引用计数都加 1,这显然不是希望看到的结果。

自动释放池

自动释放池就是一个存放对象的容器, 自动释放池会保证延迟释放池中所有的对象. 、

- (id)autorelease: 该方法不会改变对象的引用计数, 只是将对象添加到自动释放池中

自动释放池预设了在将来某一个时间将要调用relese方法, 将对象的引用计数减一, 当自动释放池释放时 ,自动释放池中所有的对象都会执行 release 方法

相关推荐
gechunlian882 小时前
Spring Security 官网文档学习
java·学习·spring
2401_853448232 小时前
Maix例程代码学习
学习
FPGA小迷弟2 小时前
FPGA工程师面试题汇总(二)
学习·fpga开发·verilog·fpga
小龙报2 小时前
【数据结构与算法】栈和队列的综合应用:1.用栈实现队列 2.用队列实现栈 3.设计循环队列
c语言·数据结构·数据库·c++·redis·算法·缓存
Aurorar0rua2 小时前
CS50 x 2024 Notes C - 01
c语言·学习方法
arvin_xiaoting2 小时前
OpenClaw学习总结_I_核心架构_9:Multi-Agent详解
网络·学习·架构·系统架构·ai agent·multi-agent·openclaw
senijusene3 小时前
依赖51 单片机的 Modbus 协议温度采集与外设控制系统的实现
c语言·单片机·嵌入式硬件·51单片机·keil
倾心琴心3 小时前
【agent辅助pcb routing coding学习】实践7 length matching 算法学习
学习·算法·agent·pcb·routing
weiabc3 小时前
今日C/C++学习笔记20260223
c语言·c++·学习