__autoreleasing

作者: SylvaChen | 来源:发表于2019-02-21 10:38 被阅读7次

    https://blog.csdn.net/li198847/article/details/50517715

    https://blog.csdn.net/junjun150013652/article/details/53149145

    https://www.jianshu.com/p/ec2c854b2efd

    1、声明NSError *__autoreleasing error;可以节省编译器的工作(参考下方)

    比如常用的NSError的使用:

    NSError *__autoreleasing error; 

    if (![data writeToFile:filename options:NSDataWritingAtomic error:&error]) 

    NSLog(@"Error: %@", error); 

    }

    (在上面的writeToFile方法中error参数的类型为(NSError *__autoreleasing *))

    注意,如果你的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的。

    最后一点,某些类的方法会隐式地使用自己的autorelease pool,在这种时候使用__autoreleasing类型要特别小心。

    比如NSDictionary的[enumerateKeysAndObjectsUsingBlock]方法:

    - (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error

    {

        [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){

    // do stuff 

              if (there is some error && error != nil)

              {

                    *error = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil];

              }

        }];

    }

    会隐式地创建一个autorelease pool,上面代码实际类似于:

    - (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error

    {

        [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){

              @autoreleasepool  // 被隐式创建

          {

                  if (there is some error && error != nil)

                  {

                        *error = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil];

                  }

              }

        }];

    // *error 在这里已经被dict的做枚举遍历时创建的autorelease pool释放掉了 :( 

    为了能够正常的使用*error,我们需要一个strong型的临时引用,在dict的枚举Block中是用这个临时引用,保证引用指向的对象不会在出了dict的枚举Block后被释放,正确的方式如下:

    - (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error

    {

    __block NSError* tempError; // 加__block保证可以在Block内被修改 

      [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)

    if (there is some error) 

    *tempError = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil]; 

    }  

    }] 

    if (error != nil) 

    *error = tempError; 

      }

    }

    2、方法用临时变量返回和不用临时变量的区别

    + (id)Object

    {

    NSMutableArray *marr = [NSMutableArray array];

    return marr;

    }

    + (id)Object

    {

    return [NSMutableArray array];

    }

    在于临时变量在接收端赋值时会进行优化,不会入栈autoreleasepool,而不声明临时变量的返回值则(根据优化规则有可能)会入栈autoreleasepool增加成本和延长声明周期。

    对函数返回值进行优化

    相关文章

      网友评论

        本文标题:__autoreleasing

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