zoukankan      html  css  js  c++  java
  • iOS ARC内存管理

    iOS的内存管理机制,只要是iOS开发者,不管多长的时间经验,都能说出来一点,但是要深入的理解。还是不简单的。随着ARC(自动管理内存)的流行。iOS开发者告别了手动管理内存的复杂工作。但是自动管理内存也不是万能的,一不小心还是会出现内存泄漏的问题。本文探讨一下iOS下的自动管理内存机制。


    ARC的本质

    ARC是编译器特性,而不是运行时特性,更不是垃圾回收机制(GC)

    ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的繁琐,编译器会自动在适当的地方插入适当的retain,release,autorelease语句。你不在需要担心内存管理,因为编译器替你处理了一切。ARC是编译器特性,而不是运行时特性(除了weak指针系统)也不是类似于其他语言中的垃圾回收机制。因此ARC和手动内存管理性能是一样的,有时还能更快加速,因为编译器还可以执行某些优化。

    ARC的开启和禁止

    (1).使用Xcode的自动转换工具
    (2).手动设置某些文件支持ARC

    1.Xcode的自动转换工具

    Xcode带了一个自动转换工具,可以将旧的源代码转成ARC模式
    (1).ARC是LLVM 3.0编译器的特性,而现有工程可能使用老的GCC 4.2或LLVM-GCC编译器,因此首先需要设置使用LLVM 3.0编译器:

    201621692751997.jpg (671×555)

    最好也选上Warnings中的Other Warning Flags 为 -Wall,这样编译器就会检查所有可能的警告,有助于我们避免潜在的问题.

    (2).设置"Objective-C Automatic Reference Counting"选项为YES,不过Xcode自动转换工具会自动设置这个选项,这里只是说明一下如何手动设置

    201621692809705.png (453×151)

    2.手动禁制某些文件的ARC -fno-objc-arc

    201621693034360.png (576×243)

    ARC的原理

    ARC的原理非常简单:只要还有一个变量指针指向对象,对象就会保存在内存中。当指针指向新值,或者指针不再存在的时候,相关联的对象就会被自动释放。这条规则对于实例变量。syncthesize属性,局部变量都是适用的。

    Strong指针

    表示引用为强引用。对应在定义property时的"strong"。所有对象只有当没有任何一个强引用指向时,才会被释放。

    注意:如果在声明引用时不加修饰符,那么引用将默认是强引用。当需要释放强引用指向的对象时,需要将强引用置nil。

    @property (nonatomic,strong) UITextField *textField;//1
    
    self.textField.text = @"tian";//2
    
    NSString *name = self.textField.text;//3

    第一行 声明一个Strong指针类型的TextField;

    执行完第二行代码,textField的text属性就是NSString对象的指针,也就是拥有者。该对象保存了文本输入框的内容。也就是说有一个强指针指向了@"tian";

    执行完第三行代码,同时有两个指针指向@"name"对象。一个对象可以拥有多个指针。name变量也指向了@"tian"对象.

    下面是李明杰老师的博客内容 图文并茂。更好理解。我偷过来。哈哈。

    控制器中有个文本输入框框属性

    @property (nonatomic, assign) IBOutlet UITextField *nameField; 

    1.如果用户在文本框中输入mj这个字符串

    那么可以这样说,nameField的text属性是NSString对象的指针,也就是拥有者,该对象保存了文本输入框的内容。

    2.如果执行了如下的代码

    NSString *name = self.nameField.text; 

    一个对象可以有多个拥有者,在上面的代码中,name变量同样也是这个NSString对象的拥有者,也就是有两个指针指向同一个对象

    3.随后用户改变了输入框的内容

    此时nameFeild的text属性就指向了新的NSString对象。但原来的NSString对象仍然还有一个所有者(name变量),因此会继续保留在内存中

     

    4.当name变量获得新值,或者不再存在时(如局部变量方法返回时、实例变量对象释放时),原先的NSString对象就不再拥有任何所有者,retain计数降为0,这时对象会被释放
    如,给name变量赋予一个新值

    name = @"Jake"; 

     

    我们称name和nameField.text指针为"Strong指针",因为它们能够保持对象的生命。默认所有实例变量和局部变量都是Strong指针

    weak

    weak型指针变量仍然可以指向一个对象,但不属于对象的拥有者。

    表示引用为弱引用。对应在定义property时用的"weak"。弱引用不会影响对象的释放,即只要对象没有任何强引用指向,即使有100个弱引用对象指向也没用,该对象依然会被释放。不过好在,对象在被释放的同时,指向它的弱引用会自动被置nil,这个技术叫zeroing weak pointer。这样有效得防止无效指针、野指针的产生。__weak一般用在delegate关系中防止循环引用或者用来修饰指向由Interface Builder编辑与生成的UI控件。

    1.执行下面的代码

    __weak NSString *name = self.nameField.text; 

    name变量和nameField.text属性都指向同一个NSString对象,但name不是拥有者

     2.如果文本框的内容发生变化,则原先的NSString对象就没有拥有者,会被释放,此时name变量会自动变成nil,称为空指针

    3.weak指针主要用于“父-子”关系,父亲拥有一个儿子的strong指针,因此父亲是儿子的所有者;但为了阻止所有权循环,儿子需要使用weak指针指向父亲。典型例子是delegate模式,你的ViewController通过strong指针(self.view)拥有一个UITableView, UITableView的dataSource和delegate都是weak指针,指向你的ViewController

    strong和weak指针的使用注意

    1.下面代码是有问题的:

    __weak NSString *str = [[NSString alloc] initWithFormat:@"1234"];  
    NSLog(@"%@", str); // 打印出来是"(null)"  

    tr是个weak指针,所以NSString对象没有拥有者,在创建之后就会被立即释放。Xcode还会给出警告("Warning: Assigning retained object to weak variable; object will be released after assignment")

    2.一般的指针变量默认就是strong类型的,因此一般我们对于strong变量不加__strong修饰,以下两行代码是等价的:

    NSString *name = self.nameField.text;  
    __strong NSString *name = self.nameField.text;  

    3.属性可以是strong或weak,写法如下

    @property (nonatomic, strong) NSString *name;  
    @property (nonatomic, weak) id delegate;  

    4.以下代码在ARC之前是可能会行不通的,因为在手动内存管理中,从NSArray中移除一个对象时,这个对象会发送一条release消息,可能会被立即释放。随后NSLog()打印该对象就会导致应用崩溃

    id obj = [array objectAtIndex:0];  
    [array removeObjectAtIndex:0];  
    NSLog(@"%@", obj);  

    在ARC中这段代码是完全合法的,因为obj变量是一个strong指针,它成为了对象的拥有者,从NSArray中移除该对象也不会导致对象被释放

    __autoreleasing

    表示在autorelease pool中自动释放对象的引用,和MRC时代autorelease的用法相同。定义property时不能使用这个修饰符,任何一个对象的property都不应该是autorelease型的。

    一个常见的误解是,在ARC中没有autorelease,因为这样一个“自动释放”看起来好像有点多余。这个误解可能源自于将ARC的“自动”和autorelease“自动”的混淆。其实你只要看一下每个iOS App的main.m文件就能知道,autorelease不仅好好的存在着,并且变得更fashion了:不需要再手工被创建,也不需要再显式得调用[drain]方法释放内存池。

    int main(int argc, char * argv[]) {
        @autoreleasepool {
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }

    以下两行代码的意义是相同的。

    NSString *str = [[[NSString alloc] initWithFormat:@"hehe"] autorelease]; // MRC
    NSString *__autoreleasing str = [[NSString alloc] initWithFormat:@"hehe"]; // ARC

    __autoreleasing在ARC中主要用在参数传递返回值(out-parameters)和引用传递参数(pass-by-reference)的情况下。

    NSError *__autoreleasing error; 
    if (![data writeToFile:filename options:NSDataWritingAtomic error:&error]) 
    { 
      NSLog(@"Error: %@", error); 
    }

    注意,如果你的error定义为了strong型,那么,编译器会帮你隐式地做如下事情,保证最终传入函数的参数依然是个__autoreleasing类型的引用。

    NSError *error; 
    NSError *__autoreleasing tempError = error; // 编译器添加 
    if (![data writeToFile:filename options:NSDataWritingAtomic error:&tempError]) 
    { 
      error = tempError; // 编译器添加 
      NSLog(@"Error: %@", error); 
    }

    所以为了提高效率,避免这种情况,我们一般在定义error的时候将其(老老实实地=。=)声明为__autoreleasing类型的:

    NSError *__autoreleasing error;

    在这里,加上__autoreleasing之后,相当于在MRC中对返回值error做了如下事情:

    *error = [[[NSError alloc] init] autorelease];

    *error指向的对象在创建出来后,被放入到了autoreleasing pool中,等待使用结束后的自动释放,函数外error的使用者并不需要关心*error指向对象的释放。

    另外一点,在ARC中,所有这种指针的指针 (NSError **)的函数参数如果不加修饰符,编译器会默认将他们认定为__autoreleasing类型。

    比如下面的两段代码是等同的:

    - (NSString *)doSomething:(NSNumber **)value
    {
            // do something  
    }
    
    - (NSString *)doSomething:(NSNumber * __autoreleasing *)value
    {
            // do something  
    }

    除非你显式得给value声明了__strong,否则value默认就是__autoreleasing的。

    ARC小结
    1.有了ARC,我们的代码可以清晰很多,你不再需要考虑什么时候retain或release对象。唯一需要考虑的是对象之间的关联,也就是哪个对象拥有哪个对象?
    2.ARC也有一些限制:
    1> 首先ARC只能工作于Objective-C对象,如果应用使用了Core Foundation或malloc()/free(),此时还是需要你来手动管理内存
    2> 此外ARC还有其它一些更为严格的语言规则,以确保ARC能够正常地工作
    3.虽然ARC管理了retain和release,但并不表示你完全不需要关心内存管理的问题。因为strong指针会保持对象的生命,某些情况下你仍然需要手动设置这些指针为nil,否则可能导致应用内存不足。无论何时你创建一个新对象时,都需要考虑谁拥有该对象,以及这个对象需要存活多久
    4.ARC还能很好地结合C++使用,这对游戏开发是非常有帮助的。对于iOS 4,ARC有一点点限制(不支持weak指针),但也没太大关系

  • 相关阅读:
    vscode .vue文件中不能提示html代码
    vscode不能提示已经定义的类名和id?
    vue常用指令
    移动端性能陷阱和硬件加速
    关于样式的获取问题
    “'npm' 不是内部或外部命令,也不是可运行的程序”
    JavaScript之闭包问题
    Sublime Text 套件介紹:Pretty JSON
    教你解决Sublime Text中文乱码问题
    C# 连接Mysql 字符串
  • 原文地址:https://www.cnblogs.com/huanying2000/p/6165440.html
Copyright © 2011-2022 走看看