• Objective C 总结(十四):内存管理


    Objective-C的对象都是动态创建的,Cocoa 采用了引用计数的技术进行对象生存周期的管理,对象新创建时引用计数为1,发送remain消息后对象引用计数+1,发送release消息后对象引用计数-1,当对象引用计数为0时,Objective-C运行会向对象发送dealloc消息进行销毁对象回收内存。注意:自己可以重写dealloc方法,但不要自己调用它。

    Objective-C提供了三种内存管理方式:manual retain-release(MRR,手动管理),automatic reference counting(ARC,自动引用计数),garbage collection(垃圾回收)。iOS不支持垃圾回收;ARC作为苹果新提供的技术,苹果推荐开发者使用ARC技术来管理内存。

    NSObject提供了基本的引用计数管理方法,

    - (id) retain;
    - (void) release;
    - (unsigned) retainCount;
    - (id) autorelease;
    int main (int argc, const char *argv[])
    {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
     
    // do something
     
    [pool release];
    return (0);
    } // main

    NSAutoreleasePool相当于一个对象集合类,在发送release消息时会向池中所有对象发送release消息。提供了简洁的写法

    @autoreleasepool {
        // Code that creates autoreleased objects.
    }

    管理规则:

    1. 用new, alloc, copy创建的对象,初始引用计数为1,当不再需要对象时,需要向对象发送release消息。
    2. retain了某个对象,要负责release这个对象。
    3. 工厂方法创建的对象,要应用autorelease消息,不需要向对象发送release了。 
      + nameWithString: (NSString *)str
      {
          return [[NSString alloc] initWithString: str] autorelease];
      }
    4. NSAutoreleasePool的release时间是确定的,要么在收到release时release,要么在事件循环结束时使用AppKit release。
    5. 在Mac OS X平台上提供的垃圾回收器,启用了后内存管理的指令都会变成空指令。垃圾回收器与NSAutoreleasePool一样,也是在事件循环结束时触发的。

    ARC

    ARC is supported in Xcode 4.2 for OS X v10.6 and v10.7 (64-bit applications) and for iOS 4 and iOS 5. Weak references are not supported in OS X v10.6 and iOS 4.

    ARC的一个基本原则: 只要某个对象被任一strong指针引用,那么它将不会被销毁。当对象没有被任何strong指针引用时,那么就将被销毁。

    ARC强制执行一些新的规则:

    1. 你不能再调用 dealloc 或者实现、调用 retain, release, retainCount, autorelease, 同样@selector(retain), @selector(release)也是不允许的, 当然你可以实现 dealloc 方法,来管理你的实例变量,或者你会调用 [systemClassInstance setDelegate:nil]等.定制的 dealloc 方法不需要写 [super dealloc],这个动作会默认调用。
      ARC只在Cocoa框架中有效,其它框架仍然需要自己管理,如 CFRetain, CFRelease 和相关Core Foundation方法。 
    2. 你不能使用 NSAllocateObject 或 NSDeallocateObject,创建对象用 alloc,运行时负责release对象。
    3. 你不能在 C 结构体中使用对象指针,因此下面代码是不可用的
      typedef struct {
          UIImage *selectedImage;
          UIImage *disabledImage;
      } ButtonImages;
    4. 不能随意在 id 与 void * 之间随意转换。编译器同样是无法管理 void * 这类 Core Foundation类型的东东,都要用 Managing Toll-Free Bridging 进行生命同期的管理。
    5. 你不能再使用 NSAutoreleasePool 对象,ARC 提供了性能更好的 @autoreleasepool block 替换原来这类使用方式。
    6. 你不能使用内存区。你不需要再使用 NSZone 等这类对象,因为现在Objective-C运行时已经忽略NSZone了,所以没必要再使用NSZone了
    7. 不能使用 new 开头的属性名,但你可以手动指定 getter 方法名
      // 非法:
      @property NSString *newTitle;
      
      // OK:
      @property (getter=theNewTitle) NSString *newTitle;

    ARC引入了新的对象生存周期修饰

    __strong
    __weak
    __unsafe_unretained
    __autoreleasing
    // 修饰属性时不需要__前缀,修饰局部变量是要加__前缀
    @property(strong) MyClass *myObject; @property(weak) MyClass *myObject;
    1. strong。是默认使用的修饰。对象保持alive,与strong指针一样的生存周期。
    2. weak。指定一个引用,但不会保持对象alive,当weak引用到一个没有strong引用的对象时,weak引用会设置为nil。
    3. unsafe_unretained。指定一个引用,但不会保持对象alive,当引用的对象没有strong引用时,不会设置为nil。
    4. autoreleasing。用于指示参数按引用传递(id *)并且在返回时autoreleased。

    当在stack上使用__weak变量时要小心,看下面代码

    NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
    NSLog(@"string: %@", string);

    字符串在实例化后没有strong指针引用它,因此它马上就被释放了,在NSLog中string变量的值就是nil。

    按引用传递对象时也要小心,看下面代码

    NSError *error;
    BOOL OK = [myObject performOperationWithError:&error];
    if (!OK) {
        // Report the error.
        // ...

    error默认是

    NSError * __strong e;

    方法声明是这样的

    - (BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

    编译器重写后的代码就是

    NSError * __strong error;
    NSError * __autoreleasing tmp = error;
    BOOL OK = [myObject performOperationWithError:&tmp];
    error = tmp;
    if (!OK) {
        // Report the error.
        // ...

    当本地变量声明(strong *error)和函数的参数((NSError * autoreleasing )error)不匹配的时候,编译器会创建一个临时变量。当你获得一个strong变量的地址时,你可以初始化一个id strong 的指针来声明 ,这样你就可以获得指针的原型,或者你可以声明一个变量为 __autoreleasing。

    避免strong引用循环

    你可以使用生命周期修饰符来避免Strong引用周期。例如,当你制作了一组父子结构的对象,而且父类要引用子类,则会出现Strong引用周期;反之,当 你将一个父类指向子类为strong引用,子类指向父类为weak引用,就可以避免出现Strong引用周期。当对象包含block objects时,这样的情况会变的更加隐性。

    在手动内存管理模式下, __block id x; x不会被 retaining,在ARC模式下,__block id x , 默认被retaining。为了使手动内存管理模式代码可以在ARC模式下正常工作, 你可以用 __unsafe_unretained 来修饰__block id x;。就和”unsafe_unretained”字面上的意思一样, 不过,这样一个non-retained变量是危险的(因为它会变成一个野指 针) 会带来不良后果。有两种更好一点的方法来处理,一是使用weak (当你不需要支持iOS 4或OS X v10.6), 二是设__block值为nil,结束他的生命周期。

    MyViewController *myController = [[MyViewController alloc] init…];
    // ...
    myController.completionHandler =  ^(NSInteger result) {
       [myController dismissViewControllerAnimated:YES completion:nil];
    };
    [self presentViewController:myController animated:YES completion:^{
       [myController release];
    }];

    你可以使用 __block修饰符然后设置myController的值为nil 替代上面的方式:

    MyViewController * __block myController = [[MyViewController alloc] init…];
    // ...
    myController.completionHandler =  ^(NSInteger result) {
        [myController dismissViewControllerAnimated:YES completion:nil];
        myController = nil;
    };

    再者,你也可以使用一个__weak临时变量

    MyViewController *myController = [[MyViewController alloc] init…];
    // ...
    MyViewController * __weak weakMyViewController = myController;
    myController.completionHandler =  ^(NSInteger result) {
        [weakMyViewController dismissViewControllerAnimated:YES completion:nil];
    };
    MyViewController *myController = [[MyViewController alloc] init…];
    // ...
    MyViewController * __weak weakMyController = myController;
    myController.completionHandler =  ^(NSInteger result) {
        MyViewController *strongMyController = weakMyController;
        if (strongMyController) {
            // ...
            [strongMyController dismissViewControllerAnimated:YES completion:nil];
            // ...
        }
        else {
            // Probably nothing...
        }
    };

    有些情况,如果类不兼容__weak时可以使用__unafe_unretained,

    Stack变量初始化为nil

    strong, weak, autoreleasing stack变量隐式初始化为nil,一般就是指局部变量

    - (void)myMethod {
        NSString *name;
        NSLog(@"name: %@", name);
    }

    使用编译器标记启用或禁用ARC

    启用-fobjc-arc

    禁用-fno-objc-arc

    Managing Toll-Free Bridging

    由于ARC不能管理Core Foundation Object的生命周期,所以在Core Foundation和ARC之间,我们需要使用到bridge,bridge_retained和__bridge_transfer三个转换关键字。

    1. __bridge,转换类型指针,不转换内存管理权
    2. __bridge_retained 或 CFBridgingRetain 将Objective-C类型转换成Core Foundation类型,同样也转移内存管理权,后续需要使用CFRelease或者相关方法来释放对象;
    3. __bridge_transfer or CFBridgingRelease 将非Objective-C类型转换成Objective-C类型,同时转移内存管理到ARC。
    - (void)logFirstNameOfPerson:(ABRecordRef)person {
     
        NSString *name = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
        NSLog(@"Person's first name: %@", name);
        [name release];
    }

    可以用下面代码替换

    - (void)logFirstNameOfPerson:(ABRecordRef)person {
     
        NSString *name = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
        NSLog(@"Person's first name: %@", name);
    }

    编译器会处理从Cocoa返回的CF对象

    NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
    [colors addObject:(id)[[UIColor lightGrayColor] CGColor]];

    使用Ownership Keywords转换方法参数

    NSArray *colors = [NSArray arrayWithObjects: [UIColor darkGrayColor], nil];
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
  • 相关阅读:
    上传gdb文件地理数据库所有图层到企业级空间库
    导出所有图层到gdb文件地理数据库
    sparkSQL报错org.apache.spark.sql.AnalysisException: Unable to generate an encoder for inner class `cn.itcast.spark.sql.Intro$People` without access to the scope that this class was defined in.
    java开发环境搭建
    qw
    SSM集成支付宝
    三次握手,四次挥手
    ASP.NET Core中的依赖注入#
    char是Java原始类型。char变量可以存储一个Unicode字符
    JAVA教程
  • 原文地址:https://www.cnblogs.com/iprogrammer/p/3247892.html
Copyright © 2020-2023  润新知