iOS进阶之ARC

目录


由编译器自动管理引用计数。通过对代码的静态分析,在合适的位置添加retain、release。

打开/关闭 ARC环境

项目 | Build Phases | Compile Sources 下文件右方添加flag:
  关闭ARC:  -fno-objc-arc
  打开ARC:  -fobjc-arc

ARC修饰符

    __strong              强引用(默认)
    __weak                弱引用(用来避免循环引用)
    __autoreleasing
    __unsafe_unretained   已废弃

使用格式
  ClassName * qualifier variableName;
  例
  NSString * __weak str = @"hello"; // 正确!
  __weak NSString *str = @"hehe";  // 错误!,但编译器不会报错,会自动进行相应处理。
__autoreleasing
    在ARC中主要用在参数传递返回值和引用传递参数。


为了避免编译器做额外处理,即为了提高效率,+ __autoreleasing
例1
  NSError *__autoreleasing error; 
  if (![data writeToFile:filename options:NSDataWritingAtomic error:&error]) 
  { 
  NSLog(@"Error: %@", error); 
  }
  如果不加__autoreleasing,则编译器会进行如下处理:
  NSError *error; 
  NSError *__autoreleasing tempError = error; // 编译器添加 
  if (![data writeToFile:filename options:NSDataWritingAtomic error:&tempError]) 
  { 
  error = tempError; // 编译器添加 
  NSLog(@"Error: %@", error); 
  }
例2
  - (NSString *)doSomething:(NSNumber **)value
  {
        // do something  
  }
  如果不加__autoreleasing,则编译器会进行如下处理:
  - (NSString *)doSomething:(NSNumber * __autoreleasing *)value
  {
        // do something  
  }
例3
  某些类的方法会隐式地创建autorelease pool
  NSError *__autoreleasing error;
  [self loopDictionary:@{@"s":@"s"} error:&error];
  - (void)loopDictionary:(NSDictionary *)dict error:(NSError **)error
  {
    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
        *error=[NSError errorWithDomain:@"s" code:1 userInfo:nil];
    }];
    NSLog(@"%@",*error);  // 崩溃,error已经被释放
  }
  编译器会自动在enumerateKeysAndObjectsUsingBlock中做处理
  NSError *__autoreleasing error;
  [self loopDictionary:@{@"s":@"s"} error:&error];
  - (void)loopDictionary:(NSDictionary *)dict error:(NSError **)error
  {
    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
      @autoreleasepool{   // 被隐式创建
        *error=[NSError errorWithDomain:@"s" code:1 userInfo:nil];
      }
    }];
    NSLog(@"%@",*error);  // 崩溃,error已经被释放
  }
  修正
  NSError *__autoreleasing error;
  [self loopDictionary:@{@"s":@"s"} error:&error];
  - (void)loopDictionary:(NSDictionary *)dict error:(NSError **)error
  {
    __block NSError *tempError;
    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
      //@autoreleasepool{   // 被隐式创建
        tempError=[NSError errorWithDomain:@"s" code:1 userInfo:nil];
      //}
    }];
    *error=tempError;
    NSLog(@"%@",*error);
  }


以下代码意义相同
  NSString *str = [[[NSString alloc] initWithFormat:@"hehe"] autorelease]; // MRC
  NSString *__autoreleasing str = [[NSString alloc] initWithFormat:@"hehe"]; // ARC
__unsafe_unretained
    现在可以完全忽略掉该修饰符,因为iOS 4已彻底退出历史舞台。
    为了在ARC刚发布时(iOS 5引入)兼容iOS 4以及版本更低的设备,因为这些版本的设备没有weak pointer system(在weak引用指向对象被释放后,把引用值自动设为nil的系统)。
    可以理解为MRC时代的assign:只是引用该对象,不做任何额外的操作。
    在引用的对象被释放时依然原原本本地指向原来被释放的对象(所在的内存区域),所以非常不安全。

block

block会隐式地对进入其作用域内的对象加retain,来确保block使用到该对象时能够正确的访问。

MRC环境下

+ __block :
    1、变量可改
    2、不做隐式的retain,这样避免了循环引用

ARC环境下

+ __block :
    变量可改

避免循环引用,使用__weak。
这里又引出一个新问题,被引用的对象可能提前释放(多线程时),解决:

    typeof(self) __weak weakSelf=self;
    self.myBlock = ^{
        typeof(self) __strong bSelf=weakSelf;    // 强引用,block执行完毕则销毁,不会造成循环引用
        NSLog(@"%@",bSelf);
    };

Core Foundation不支持ARC

__bridge

只是声明类型转变,但是不做内存管理规则的转变
    CFStringRef s1 = (__bridge CFStringRef) [[NSString alloc] initWithFormat:@"HelloWorld!"];

ARC->MRC

    NSString *str = [[NSString alloc] initWithFormat:@"HelloWorld!"];
//    CFStringRef s2 = (__bridge_retained CFStringRef)str;
    CFStringRef s2 = (CFStringRef)CFBridgingRetain(str);
    //...
    CFRelease(s2);

MRC->ARC

    CFStringRef result = CFURLCreateStringByReplacingPercentEscapes(, , );
    NSString *s = (__bridge_transfer NSString *)result;

你可能感兴趣的:(iOS进阶之ARC)