内存分配区域:Stack和Heap
Stack和Heap是内存管理的两个重要概念(这里指的不是数据结构上的栈和堆)。这里指的是内存分配的区域。
1)Stack中的内存由操作系统进行分配
现代操作系统中,启动一个线程就会分配一个Stack内存,当一个函数被调用,就会有一个Stack frame(栈帧)被压到Stack内存中。里面包含这个函数所涉及到的参数、局部变量、返回地址等相关信息。当函数执行完毕后,这个栈帧就会被销毁。这一切都是系统自动帮我们进行分配和销毁的。
2)Heap中的内存需要手动分配
Heap与动态分配相关,内存可以随时在堆中分配和销毁。我们需要明确请求内存的分配和销毁,即malloc和free。
一个对象在alloc的时候,就在Heap中分配了内存空间。
从上面可以看出Stack中的对象通常有速度上的优势,而且不会发生内存泄露问题。那么为什么OC中的对象不使用Stack内存管理方式,而使用Heap的内存管理方式呢,原因在于:
1) Stack中对象的生命周期所导致的问题。例如:一旦函数返回(结束),则所在的Stack frame就会被销毁,那么此时返回的对象也会一并销毁。这个时候我们在去retain这个对象是无效的。简单而言,就是Stack中对象的生命周期不适合OC的引用计数内存管理方法。
2) Stack中的对象不够灵活。不具备足够的扩展性。创建时长度已经是固定的,而Stack中对象的拥有者也就是所在的Stack frame。
引用计数器
前面说到,OC对象都是分配到Heap内存中的。如果我们手动创建了一个对象,在Heap中占有了内存,我们使用后,是如何判断该对象应该被销毁呢(回收内存等资源)?
我们都知道在C#、Java中都有GC在自动管理内存,当我们实例化一个对象之后通常会有一个变量来引用这个对象(变量中存储对象地址),当这个引用变量不再使用之后(也就是不再引用这个对象)此时GC就会自动回收这个对象,简单的说就是:当一个对象没有任何变量引用的时候就会被回收。
如何使用@property进行自动实现呢?答案就是使用属性参数
你会发现此刻我们不必手动实现getter、setter方法程序仍然没有内存泄露
@property的参数分为三类,也就是说参数最多可以有三个,中间用逗号分隔,每类参数可以从上表三类参数中人选一个。如果不进行设置或者只设置其中一类参数,程序会使用三类中的各个默认参数,默认参数:(atomic,readwrite,assign)
在ObjC中也有一种内存自动释放的机制叫做“自动引用计数”(或“自动释放池”),与C#、Java不同的是,这只是一种半自动的机制,有些操作还是需要我们手动设置的。自动内存释放使用@autoreleasepool关键字声明一个代码块,如果一个对象在初始化时调用了autorelase方法,那么当代码块执行完之后,在块中调用过autorelease方法的对象都会自动调用一次release方法。这样一来就起到了自动释放的作用,同时对象的销毁过程也得到了延迟(统一调用release方法)。
Stack和Heap是内存管理的两个重要概念(这里指的不是数据结构上的栈和堆)。这里指的是内存分配的区域。
1)Stack中的内存由操作系统进行分配
现代操作系统中,启动一个线程就会分配一个Stack内存,当一个函数被调用,就会有一个Stack frame(栈帧)被压到Stack内存中。里面包含这个函数所涉及到的参数、局部变量、返回地址等相关信息。当函数执行完毕后,这个栈帧就会被销毁。这一切都是系统自动帮我们进行分配和销毁的。
2)Heap中的内存需要手动分配
Heap与动态分配相关,内存可以随时在堆中分配和销毁。我们需要明确请求内存的分配和销毁,即malloc和free。
OC中的Stack和Heap:
所有OC对象都是分配在Heap内存中的。在OC最典型的内存分配和初始化操作如下:
NSObject *obj = [[NSObject alloc] init];一个对象在alloc的时候,就在Heap中分配了内存空间。
从上面可以看出Stack中的对象通常有速度上的优势,而且不会发生内存泄露问题。那么为什么OC中的对象不使用Stack内存管理方式,而使用Heap的内存管理方式呢,原因在于:
1) Stack中对象的生命周期所导致的问题。例如:一旦函数返回(结束),则所在的Stack frame就会被销毁,那么此时返回的对象也会一并销毁。这个时候我们在去retain这个对象是无效的。简单而言,就是Stack中对象的生命周期不适合OC的引用计数内存管理方法。
2) Stack中的对象不够灵活。不具备足够的扩展性。创建时长度已经是固定的,而Stack中对象的拥有者也就是所在的Stack frame。
引用计数器
前面说到,OC对象都是分配到Heap内存中的。如果我们手动创建了一个对象,在Heap中占有了内存,我们使用后,是如何判断该对象应该被销毁呢(回收内存等资源)?
我们都知道在C#、Java中都有GC在自动管理内存,当我们实例化一个对象之后通常会有一个变量来引用这个对象(变量中存储对象地址),当这个引用变量不再使用之后(也就是不再引用这个对象)此时GC就会自动回收这个对象,简单的说就是:当一个对象没有任何变量引用的时候就会被回收。
但是在ObjC中没有垃圾回收机制,那么ObjC中内存又是如何管理的呢?其实在ObjC中内存的管理是依赖对象引用计数器来进行的:在ObjC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后它的引用计数器为1,当调用这个对象的alloc、retain、new、copy方法之后引用计数器自动在原来的基础上加1(ObjC中调用一个对象的方法就是给这个对象发送一个消息),当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。
我们在开发过程中手动管理内存有时候并不容易,因为对象的引用有时候是错综复杂的,对象之间可能互相交叉引用,此时需要遵循一个法则:谁创建,谁释放。
Xcode也为我们提供了很多条件来帮助我们管理内存。
1.属性参数如何使用@property进行自动实现呢?答案就是使用属性参数
你会发现此刻我们不必手动实现getter、setter方法程序仍然没有内存泄露
@property的参数分为三类,也就是说参数最多可以有三个,中间用逗号分隔,每类参数可以从上表三类参数中人选一个。如果不进行设置或者只设置其中一类参数,程序会使用三类中的各个默认参数,默认参数:(atomic,readwrite,assign)
一般情况下如果在多线程开发中一个属性可能会被两个及两个以上的线程同时访问,此时可以考虑atomic属性,否则建议使用nonatomic,不加锁,效率较高
在ObjC中也有一种内存自动释放的机制叫做“自动引用计数”(或“自动释放池”),与C#、Java不同的是,这只是一种半自动的机制,有些操作还是需要我们手动设置的。自动内存释放使用@autoreleasepool关键字声明一个代码块,如果一个对象在初始化时调用了autorelase方法,那么当代码块执行完之后,在块中调用过autorelease方法的对象都会自动调用一次release方法。这样一来就起到了自动释放的作用,同时对象的销毁过程也得到了延迟(统一调用release方法)。