iOS 开发基础(1)--内存管理

作者: 西风颂 | 来源:发表于2016-07-07 09:54 被阅读208次

    内存管理的作用:实质就是管理堆(自己申请,自己释放)空间上的内存;在恰当的时候销毁对象的内存空间;合理使用内存,保证程序的高效运行;

    1.内存管理的基本原则

    MRC:遵循"谁创建,谁释放;谁引用,谁管理"的机制;当创建或者引用一个对象的时候,需要向这个对象发送alloc,copy,retain或者new消息,当释放对象的时候需要向该对象发送release或autorelease消息;每个对象都有一个引用计数器,初始化的值为1,引用计数器的是OC中判断这个对象是否被销毁的唯一标准,当对象的引用计数器为0的时候才会被销毁,对象自动调用dealloc方法;

    ARC:iOS5.0/Mac OS X 10.7之后引入了自动内存管理机制----自动引用计数器;ARC的本质和MRC的实质是一样的,只是在ARC中不在显式的调用release或autorelease消息;在编译的时候编译器会自动在相应的位置加上release或autorelease;这是编译器的一个特性;同时在ARC环境下retain和release是被禁用,相应的修饰符换成了strong,在OC中默认情况下所有的指针都是强指针;在ARC环境中只要没有强指针指向对象这个对象就会被销毁;

    2.属性修饰符的使用

    使用场合 修饰符 修饰类型及作用
    strong ARC 相当于MRC下的retain,修饰OC对象
    weak ARC 修饰delegate和UI控件;所有被weak修饰的指针在对象被释放的时候都会将指针置为nil;避免循环引用的问题,同时也避免了野指针操作;
    retain MRC 引用计数器加1;指针拷贝,指向同一内存空间
    assign MRC/ARC 一般只用来修饰基本数据类型,只是简单的赋值,不改变引用计数器;有时也会用于切断循环引用的问题修饰delegate;
    copy MRC/ARC 修饰字符串,block;属于内容的拷贝(内存分配一个新的对象),指针指向不同的内存;

    符合内存管理原则的setter方法的写法:

    - (void)setName:(NSString *)name
    {
          if(_name != name){
               [_name release];
               _name = [name retain];
          }
    }
    
    - (void)setName:(NSString *)name
    {
          if(_name != name){
               [_name release];
               _name = [name copy];
          }
    }
    

    @synthesize和@dynamic分别有什么作用?

    @synthesize
    除非已经实现属性的getter和setter方法,否则由编译器自动生成getter和setter方法;当开发人员已经实现的话,编译器就不会自动生成getter和setter方法的实现;
    @dyanmic
    就是告诉编译器不自动生成getter和setter方法,避免编译期间产生警告;

    3.自动释放池

    自动释放池就是一个池子,当对象调用autorelease方法的时候,会自动将对象放在自动释放池中;只有当池子被销毁的时候,会给池子中所有的对象执行一个release方法,达到延迟释放对象的效果;

    自动释放池和垃圾回收(gc)的区别

    ios 是没有垃圾回收的,自动释放池是 oc 中管理内存的一种方式,它和 gc 是本质区别的,自动释放池管理内存的前提是,必须把要管理内存的对象加入池内,才会生效。而 gc 是不断检测当前程序中是否有不再使用的内存进而释放

    4.内存管理的注意事项

    1. _ _weak 和 __strong用来修饰变量的.

    __weak是声明一个可以自动nil化的弱引用.

    __strong是缺省的关键字.

    __unsafe_unretained声明一个弱引用,不会自动你nil化;作用相当于assign;

    2.声明属性的时候不能以new开头;如果非要以new开头的话需要自定义getter方法名,如:

    @property(getter = getName) NSString *name;
    

    3.UI控件声明属性的时候一般使用weak;因为UIWinow ----- RootViewController ----- view ----- subViews ----- 数组包含当前的控件;也就是说当前控制器已经强引用当前控件了,因此在声明属性的时候使用weak比较好;

    4.block中为了避免循环引用应使用_ _weak typeof(self) weakself = self;同时在block内部修改外部变量的时候使用 __block修饰;其作用是只要观察到该变量被block所持有,就将外部变量在栈中的内存地址放到堆中,进而可以在block内部也可以修改外部变量的值;

    5.ARC只能管理Foundation框架的变量,如果程序中把Foundation中的变量强制换成Core Foundation中的变量需要交换管理权;

    __bridge只做类型的转换,但是不修改对象的内存管理权;
    __bridge_ratained (也可以使用CFBridgingRetain)将OC对象转换成C对象,同时将对象的内存管理权将给我们,后续需要使用CGRelease或者相关方法来释放对象
    __bridge_transfer(也可以使用CFBridgingRelease)将C对象转换成OC对象,同时将对象的内存管理权交给ARC来处理;

    6.ARC和MRC混编的转换
    在非ARC工程中采用ARC去编译某些类:-fobjc-arc。
    在ARC下的工程采用非ARC去编译某些类:-fno-objc-arc。

    7.代理(delegate)声明属性的时候使用weak或者assign;这样是为了防止循环引用,因为一般情况下控件的代理都是控制器,而控制器拥有这个控件,构成了循环引用是文字;

    5.@property 的本质是什么?

    本质就是:@property = ivar + setter + getter;

    解释:
    "属性(property)有两打概念:ivar(实例变量) 存取方法(access method = setter +getter)".

    “属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。 Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。这个概念已经定型,并且经由“属性”这一特性而成为 Objective-C 2.0 的一部分。 而在正规的 Objective-C 编码风格中,存取方法有着严格的命名规范。 正因为有了这种严格的命名规范,所以 Objective-C 这门语言才能根据名称自动创建出存取方法。其实也可以把属性当做一种关键字,其表示:

    编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量。 所以你也可以这么说:@property = getter + setter;

    ivar、getter、setter 是如何生成并添加到这个类中的?
    “自动合成”( autosynthesis)
    完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这个过程由编译器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。也可以在类的实现代码里通过 @synthesize 语法来指定实例变量的名字.

    我为了搞清属性是怎么实现的,曾经反编译过相关的代码,他大致生成了五个东西:

    1. OBJC_IVAR_$类名$属性名称 :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
    1. setter 与 getter 方法对应的实现函数
    2. ivar_list :成员变量列表
    3. method_list :方法列表
    4. prop_list :属性列表

    也就是说我们每次在增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.

    相关文章

      网友评论

        本文标题:iOS 开发基础(1)--内存管理

        本文链接:https://www.haomeiwen.com/subject/ntfljttx.html