为什么管理内存:
程序在运行的时候,要创建大量的对象,这些对象放在堆和栈上。(基本类型放在栈上,由系统自动管理。) 而放在堆上的对象如果得不到及时释放,就会占用大量内存。OC中没有垃圾回收机制,所以我们要手动管理内存(ARC之前)
原理:
依赖对象引用计数器+1 -1:在ObjC中对象创建后内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后它的引用计数器为1,当调用这个对象的alloc、retain、new、copy方法之后引用计数器自动在原来的基础上加1(ObjC中调用一个对象的方法就是给这个对象发送一个消息),当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。
原则:
谁创建,谁释放,谁引用,谁管理。
自动释放池:
autorelease方法不会改变对象的引用计数器,只是将这个对象放到自动释放池中;
自动释放池(@autoreleaespool)实质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象(例如如果一个对象的引用计数器>1则此时就无法销毁);
由于自动释放池最后统一销毁对象,因此如果一个操作比较占用内存(对象比较多或者对象占用资源比较多),最好不要放到自动释放池或者kao虑放到多个自动释放池;
自动释放池是什么,如何工作
当您向一个对象发送一个autorelease消息时,将该对象的一个引用放入到最新的
自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对
象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释
放,池中的所有对象也就被释放。
什么是ARC:
官方定义:“自动引用计数(ARC)是一个编译器级的功能,它能简化Cocoa应用中对象生命周期管理(内存管理)的流程。”
Automatic Reference Counting,自动引用计数,即ARC
ARC编译器有两部分,分别是前端编译器和优化器
当ARC开启时,编译器将自动在代码合适的地方插入retain, release和autorelease
ARC优化器---当代码中出现多个对 retain 和release的重复调用,ARC优化器负责移出多余的 retain 和release语句。确保生成的代码运行速度高于手动引用计数的代码。
如果涉及到较为底层的东西,比如Core Foundation中的malloc()或者free()等,ARC就鞭长莫及了,这时候还是需要自己手动进行内存管理
打开ARC:-fobjc-arc
关闭ARC:-fno-objc-arc
关键字:
@property的参数分为三类,也就是说参数最多可以有三个
程序会使用三类中的各个默认参数,默认参数:(atomic,readwrite,assign)
atomic 对属性加锁,多线程下线程安全,默认值 加同步 是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的。对于对象的默认属性,就是setter/getter生成的方法是一个原子操作。
如果有多个线程同时调用setter的话,不会出现某一个线程执行setter全部语句
之前,另一个线程开始执行setter的情况,相关于方法头尾加了锁一样。
nonatomic 对属性不加锁,多线程下不安全,但速度快 不加同步,多线程并发访问会提高性能。
readwrite 默认属性,将生成不带额外参数的getter和setter方法(setter方法只有一个参数)。
readonly 将只生成getter方法而不生成setter方法(getter方法没有get前缀)。
assign 基本的赋值、直接赋值,默认值 不更改索引计数(Reference Counting).使用assign: 对基础
数据类型 (NSInteger)和C数据类型(int, float, double, char,等)
retain 让对象引用计数+1,表示拥有这个对象,先release原来的值,在retain新值
copy 先release原来的值,在copy新值 建立一个索引计数为1的对象,然后释放旧对象
retain是指针拷贝,copy是内容拷贝。(可以理解为retain是浅拷贝,copy是深拷贝。)
strong 强引用,strong关键字与retain关似,用了它,引用计数自动+1,有strong指向的对象不会被释放
weak 弱引用,声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。声明了一个可以自动 nil 化的弱引用
当weak指向的内存释放掉后自动置为nil,防止野指针。
unsafe_unretained声明的指针,由于 self.string1=nil已将内存释放掉了,但是string2并不知道已被释放了,所以是野指针。然后访问野指针的内存就造成crash. 声明一个弱应用,但是不会自动nil化,也就是说,如果所指向的内存区域被释放了,这个指针就是一个野指针了。
static 一般情况下,只能用NSString或者基本类型 都是私有的,通常在单例中使用
assign,用于基本数据类型
retain,通常用于非字符串对象 对其他NSObject和其子类
copy,通常用于字符串对象、block、NSArray、NSDictionary
copy是创建一个新对象,retain是创建一个指针,引用对象计数加1
强引用也就是我们通常所讲的引用,其存亡直接决定了所指对象的存亡。如果不存在指向一个对象的引用,并且此对象不再显示列表中,则此对象会被从内存中释放。
弱引用除了不决定对象的存亡外,其他与强引用相同。即使一个对象被持有无数个若引用,只要没有强引用指向他,那麽其还是会被清除。
static 关键字的作用:
函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,
因此其值在下次调用时仍维持上次的值;
在C语言中,关键字static有三个明显的作用:
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
关键字const有什么含意?修饰类呢?static的作用,用于类呢?还有extern c的作用
const 意味着"只读",下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指
针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修
改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是
不可修改的,同时指针也是不可修改的)。
结论:
关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人 来清理的。) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出 现。
(1)欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时
,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为
const,或二者同时指
定为 const;
(3)在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数
内部不能改变其值;
(4)对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不
能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返
回值不为“左值”。
volatile有什么含意?并给出三个不同的例子。
一个定义为 volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量的几个例子:
并行设备的硬件寄存器(如:状态寄存器)
一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
多线程应用中被几个任务共享的变量
ios的@property属性和@synthesize属性:
//当编译器遇到@property时,会自动展开成getter和setter的声明
@property int age;
@property int no;
//@synthesize 会自动生成getter和setter的实现
//@synthesize 默认会去访问age,no,height同名的变量,
//如果找不到同名的变量,会在内部自动生成一个私有同名变量age,no,height,,
//因此Student.h 中的这几个变量也可以省略不写。
@synthesize age,no;
1.在Xcode4.5及以后的版本中,可以省略@synthesize ,编译器会自动帮你加上getter 和 setter 方法的实现,并且默认会去访问_age这个成员变量,如果找不到_age这个成员变量,会自动生成一个叫做 _age的私有成员变量。
ios中的成员变量定义在@interface 和@implementation 中的区别是什么?
定义在@interface中是指定义在头文件里, 定义在@implementation中是指在实现文件中的类扩展(Class Extensions), 一般来说把要gong开的信息(变量,属性,方法)定义在头文件里, 把要隐藏的信息定义在类扩展里,只是为了隐藏私有信息, 不需要被外界知道的就不要放在头文件里, 这样可以隔离接口和实现。
堆和栈?
对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来讲,释放工作有程序员控制,容易产生memory Leak。
1.申请方式:stack:由系统自动分配。heap:需要程序员自己申请。
2.申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,
3.申请大小的限制:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,大小有限制。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址
4.申请效率的比较:
栈由系统自动分配,速度较快。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,
5.堆和栈中的存储内容
6.存取效率的比较:
栈的效率比较高
堆和栈的区别?
堆是程序员控制的,栈是编辑器自动管理,栈是一块连续的内存区域,是向低地址扩展的数据结构,堆是向高低直落站的数据结构。
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
参kao文献:
http://www.cnblogs.com/kenshincui/p/3870325.html#autoid-2-1-0
http://mobile.51cto.com/iphone-386301.htm
http://blog.youkuaiyun.com/getchance/article/details/42213219
http://www.cnblogs.com/csj007523/archive/2012/07/23/2605662.html
http://www.cnblogs.com/thinksasa/p/3441551.html