内存管理
1.什么情况使用weak关键字,相比assign有什么不同?
1.1什么情况使用weak关键字?
在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,不如:delegate代理属性
自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性一般也使用weak;当然,也可以使用strong。
1.2不同点
weak此特质表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。而assign的“设置方法”只会执行针对“纯量类型”(scalar type,例如CGFloat 或 NSInteger等)的简单赋值操作。
assign可以用非OC对象,而weak必须用于OC对象
2.如何让自己的类用copy修饰符?如何重写带copy关键字的setter?
2.1若想令自己所写的对象具有拷贝功能,则需要实现NSCoping协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCoping与NSMutableCopying协议。
具体步骤:
需声明该类遵从NSCopying协议
实现NSCopying协议。该协议只有一个方法:
-(id)copyWithZone:(NSZone*)zone;
注意:一提到让自己的类用copy修饰符,我们总是想覆写copy方法,其实真正需要实现的却是“copyWithZone”方法。
2.2重写带copy关键字的setter,例如:
-(void)setName:(NSString*)name{
//[_name release];
_name=[name copy];
}
3.深拷贝与浅拷贝
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
当对象中存在指针成员时,除了在复制对象时需要考虑自定义拷贝构造函数,
还应该考虑一下两种情形:
3.1当函数的参数为对象时,实参传递给形参的实际上是实参的一个拷贝对象,系统自动通过拷贝构造函数实现;
3.2当函数的返回值为一个对象时,该对象实际上是函数内对象的一个拷贝,用于返回函数调用处。
copy方法:如果是非可扩展类对象,则是浅拷贝。如果是可扩展类对象,则是深拷贝。
mutableCopy方法:无论是可扩展类对象还是不可扩展类对象,都是深拷贝。
4.@property的本质是什么?ivar,getter,setter是如何生成并添加到这个类中的
4.1@property的本质是实例变量(ivar)+存取方法(access method = getter setter),即@property=ivar+getter+setter;
“属性”(property)作为Object-C的一项特性,主要的作用就在于封装对象中的数据。Object-C对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”来访问。其中,“获取方法”用于读取变量值,而“设置方法”用于写入变量值。
4.2ivar,getter,setter是自动合成这个类中的
完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”。需要强调的是,这个过程由编译器在编译期执行,所以编译器看不到这些“合成方法”的源代码。除了生成方法代码getter,setter之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为——firstName与——lastName。也可以在类的实现代码里通过@synthesize语法来指定实例变量的名字。
5.@protocol和category中如何使用@property
5.1在protocol中使用property只会生成setter和getter方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现的属性
5.2category使用@property也是只会生成setter和getter方法的声明,如果我们真的需要给category增加属性的实现,需要借助于运行时的两个函数:objc_setAssociatedObject和objc_getAssociatedObject
6.简要说一下@autoreleasePool的数据结构?
简单说是双向链表,每张链表头尾相接,有parent,child指针
每创建一个池子,会在首部创建一个哨兵对象,作为标记
最外层池子的顶端会有一个next指针。当链表容量满了,就会在链表的顶端,并指向下一张表。
7.BAD_ACCESS在什么情况下出现?
访问了悬垂指针,比如对一个已经释放的对象执行了release,访问已经释放对象的成员变量或者发消息。死循环
8.使用CADisplayLink,NSTimer有什么注意点?
CADisplayLink,NSTimer会造成循环引用,可以使用YYWeakProxy或者为CADisplayLink,NSTimer添加block方法解决循环引用
9.iOS内存区分情况
9.1栈区(Stack)
由编译器自动分配释放,存放函数的参数,局部变量的值等
栈是向低地址扩展的数据结构,是一块连续的内存区域
9.2堆区(Heap)
由编译器自动分配释放,存放函数的参数,局部变量的值等
堆是向高地址扩展的数据结构,是一块不连续的内存区域
9.3全局区
全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放
9.4常量区
常量字符串就是放在这里的,程序结束后由系统释放
9.5代码区
存放函数体的二进制代码
注:
1.在 iOS 中,堆区的内存是应用程序共享的,堆中的内存分配是系统负责的
2.系统使用一个链表来维护所有已经分配的内存空间(系统仅仅记录,并不管理具体的内容)
3.变量使用结束后,需要释放内存,OC 中是判断引用计数是否为 0,如果是就说明没有任何变量使用该空间,那么系统将其回收
4.当一个app启动后,代码区、常量区、全局区大小就已经固定,因此指向这些区的指针不会产生崩溃性的错误。而堆区和栈区是时时刻刻变化的(堆的创建销毁,栈的弹入弹出),所以当使用一个指针指向这个区里面的内存时,一定要注意内存是否已经被释放,否则会产生程序崩溃(也即是野指针报错)
10.iOS内存管理方式
10.1Tagged Pointer(小对象)
Tagged Pointer专门用来存储小的对象,例如NSNumber和NSDate
Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要malloc和free
在内存读取上有着3倍的效率,创建时比以前快106倍
objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据
使用Tagged Pointer后,指针内存储的数据变成了Tag+Data,也就是将数据直接存储在了指针中
10.2NONPOINTER_ISA(指针中存放与该对象内存相关的信息)苹果将ISA设计成了联合体,在ISA中存储了与该对象相关的一些内存的信息,原因也如上面所说,并不需要64个二进制位全部都用来存储指针。