本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !
作为一名开发者来说对内存管理一定不会感到陌生,那么内存管理到底是管理哪部分的内存呢?
对iOS 程序内存布局比较了解的应该会知道内存布局分为:保留区、代码段、数据段、堆区、栈区、内核区;真正需要开发者参与管理的是:在堆空间申请的内存
;如果程序员不释放,程序结束后,会由操作系统回收; 这里的结束也有可能是 崩溃导致
的结束
我多句嘴啊,为什么栈空间不需要开发者参与管理内存 ?
对啊,这是为什么呢 ? 嗯 原来是由编译器自动分配释放,所以不需要开发者参与咯 !
简单描述栈空间的内存管理原则 ?
分配:编译阶段由编译器来给参数值、局部变量等分配存储空间
释放:当超出其作用域时,由编译器控制从栈中弹出。
Manual Reference Counting :MRC, 手动引用计数
Automatic Reference Counting : ARC ,自动引用计数
-
MRC:遵循谁申请、谁添加、谁释放的原则。需要手动处理内存计数的
+1
和-1
。从12年iOS5
开始,逐步被ARC(自动引用计数)模式取代。 -
ARC取代了MRC后,在App编译阶段,由编译器(LLVM)在合适的位置添加了OC对象的内存管理代码。
OC对象的内存管理原则:
-
在iOS中,使用
引用计数
来管理OC对象的内存 -
一个新创建的OC对象引用计数默认是
1
,当引用计数减为0
时;OC对象就会销毁,释放其占用的内存空间 -
调用
retain
会让OC对象的引用计数+1
,调用release
会让OC对象的引用计数-1
内存管理的经验总结
-
当调用
alloc
、new
、copy
、mutableCopy
方法返回了一个对象,当不需要这个对象时,就要调用release
或者autorelease
来释放它 -
[NSMutableArray array]
、[NSMutableDictionary dictionary]
等创建出来的对象不需要再添加内存管理代码(release
、retain
),
因为方法内部已经实现的内存管理代码 -
想拥有某个对象,就让它的引用计数
加一
; 不想再拥有时,就让它的引用计数减一
-
MRC环境OC对象的声明
:使用retain
关键字修饰,在dealloc
的父类方法调用前释放; 不需要实现setter方法的内存管理代码,因为用retain关键字修饰后已经自动实现了setter方法、getter方法以及内存管理代码
MRC环境OC对象的声明示例
@property (nonatomic, retain)XYHCar *car;
- (void)dealloc
{
// [_car release];
// _car = nil;
//或
self.car = nil;
// 父类的dealloc放到最后
[super dealloc];
}
- 可以通过以下私有函数来查看自动释放池的情况
void _objc_autoreleasePoolPrint(void)
这很奇怪,既然是私有函数怎么能够被外界调用呢 ?怎么做到的呢 ?
可以的,使用 extern 关键字(前提是该函数没有被struct修饰为静态方法)对该函数在需要用到的地方重新声明一遍,真正调用时编译器会去查找有没有这个函数,只要有就会调用啦 ! 比如:
示例代码
传入void表示没有参数
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool { // r1 = push()
XYHOlg *olg1 = [[[XYHOlg alloc] init] autorelease];
XYHOlg *olg2 = [[[XYHOlg alloc] init] autorelease];
@autoreleasepool { // r2 = push()
for (int i = 0; i < 6; i++) {
XYHOlg *olg3 = [[[XYHOlg alloc] init] autorelease];
NSLog(@"%@",olg3);
}
@autoreleasepool { // r3 = push()
XYHOlg *olg4 = [[[XYHOlg alloc] init] autorelease];
_objc_autoreleasePoolPrint();
NSLog(@"%@,%@,%@",olg1,olg2,olg4);
} // pop(r3)
} // pop(r2)
} // pop(r1)
return 0;
}
输出结果
(hot) :当前 pool page
(cold):不是当前 pool page
(hot)(cold): 就一个pool page,怎么着都行
objc[1363]: ##############
objc[1363]: AUTORELEASE POOLS for thread 0x1000d1dc0
objc[1363]: 12 releases pending.
objc[1363]: [0x100805000] ................ PAGE (hot) (cold)
objc[1363]: [0x100805038] ################ POOL 0x100805038 第一个自动释放池
objc[1363]: [0x100805040] 0x10051fbc0 XYHOlg
objc[1363]: [0x100805048] 0x10051fc10 XYHOlg
objc[1363]: [0x100805050] ################ POOL 0x100805050 第二个自动释放池
objc[1363]: [0x100805058] 0x10051fc20 XYHOlg
objc[1363]: [0x100805060] 0x10051fc30 XYHOlg
objc[1363]: [0x100805068] 0x100520130 XYHOlg
objc[1363]: [0x100805070] 0x100520140 XYHOlg
objc[1363]: [0x100805078] 0x100520150 XYHOlg
objc[1363]: [0x100805080] 0x100520160 XYHOlg
objc[1363]: [0x100805088] ################ POOL 0x100805088 第三个自动释放池
objc[1363]: [0x100805090] 0x100520170 XYHOlg
objc[1363]: ##############
Program ended with exit code: 0
release和autorelease的区别 ?
-
release
:在使用完对象后调用 -
autorelease
:
在初始化的时候调用,
一个对象被autorelease
,则该对象内存地址
将被追加到当前任务的AutoreleasePool
(自动释放池)里,通过AutoreleasePoolPage对象
来管理,在合适的时机释放
AutoreleasePool
并没有单独的结构,而是由若干个AutoreleasePoolPage
以双向链表
的形式组合而成(分别对应结构中的parent
指针和child
指针)
ARC环境声明OC对象可以用assign来修饰吗 ?为什么?
- 当然可以用来修饰OC对象,且编译器还不会报错,但不推荐
-
assign
关键字修饰的OC对象在setter方法
中不会生成
相关的内存管理代码 -
assign
关键字修饰的对象释放后,指针不会被清空
依然指向释放前的对象;这很危险,在后续的操作中如果用到该对象会出现悬垂指针访问
导致崩溃 -
assign
: 多用来修饰基本数据类型
小小知识点:指针
注意:这里的内存管理 范围
是 OC对象
,至于基本数据类型如:int
、float
不在范围之内;
OC对象的引用计数都是自然数吗 ?
绝大多数是的,只有NSString变NSTaggedPointerString
时引用计数为其内存地址的十进制数
在开发中就没有需要用到OC对象之外的数据类型了吗 ?他们的内存管理该如何实现 ?
在iOS开发过程中可能会用到的语言有很多,比如:C
、C++
、RN
、Swift
、Javascript
等;这其中最多使用到是C的一些数组、字符串、指针, C语言中对堆内存的申请是调用malloc函数
实现、释放是调用free函数
实现;
Javascript
具有自动垃圾回收机制,执行环境会管理代码执行过程中使用的内存。
示例代码:
int _tmain(int argc, _TCHAR* argv[])
{
char *p = (char *)malloc(1024*1024*1024);//在堆中申请了内存
memset(p, 'a', sizeof(int) * 10);//初始化内存
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i + 65;
}
print_array(p, 10);
free(p);//释放申请的堆内存
}
网友评论