您现在的位置是:首页 >技术杂谈 >【iOS】NSError**和__autoreleasing场景网站首页技术杂谈

【iOS】NSError**和__autoreleasing场景

神奇阿道和小司 2023-05-17 20:00:02
简介【iOS】NSError**和__autoreleasing场景

前言

在看JSONModel源码的时候,JSONModel的自定义Error的方法一直在报错

- (BOOL)validate:(NSError *__autoreleasing *)error {
    
}

这个方法在定义error的时候添加上了__autoreleasing修饰符,涉及到了__autoleasing的显式隐式调用就去了解了一下。

发现在ARC的时候对这个修饰符学习的很浅,这里复习➕学习。

在这之前还是了解__autoreleasing修饰符的作用和原理

autoreleasing 在MRC和ARC下的应用

__autoreleasing 是一个用于 ARC(自动引用计数)的关键字,它可以在方法返回对象时自动将对象加入到 autorelease pool 中。

在ARC里,如果一个方法返回一个对象,编译器会自动生成一个 __autoreleasing 变量,并将这个对象赋值给这个变量,然后将这个变量加入到 autorelease pool 中,以便在 pool 被 drain 时释放对象。

在手动管理内存的 MRC 环境中,如果想要将一个对象加入到 autorelease pool 中,可以显式地使用 NSAutoreleasePool 的 addObject: 方法将对象添加到 pool 中,或者使用 autorelease 方法将对象加入到当前 autorelease pool 中。

使用 __autoreleasing 关键字的隐式调用是在方法返回对象时自动完成的,而显式调用是在需要手动将对象添加到 autorelease pool 中时使用的。

__autoreleasing如何做到延迟释放

顺带提一下 在定义 Objective-C 的 @property 时,不能使用 __autoreleasing 修饰符,因为该修饰符只适用于手动内存管理的情况,而在 ARC 中,对象的内存管理已经由编译器自动处理,不需要手动管理

__autoreleasing是可以延迟释放对象,在ARC里,__weak修饰的对象在声明周期结束的时候就会自动释放掉,那么就利用_autoreleasing修饰符演示如何延迟释放

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,weak) NSObject *weakObj1;
@property (nonatomic,weak) NSObject *weakObj2;
@end

@implementation ViewController
- (void)test {
     NSLog(@"--1---%@--%@",self.weakObj1,self.weakObj2);
     [self test2];
     NSLog(@"--3---%@--%@",self.weakObj1,self.weakObj2);
 }
- (void)test2 {
     __autoreleasing NSObject *obj1 = [NSObject new];
     NSObject *obj2 = [NSObject new];
     self.weakObj1 = obj1;
     self.weakObj2 = obj2;
     NSLog(@"--2---%@--%@",self.weakObj1,self.weakObj2);
 }
- (void)viewDidLoad {
    [super viewDidLoad];
    [self test];
    // Do any additional setup after loading the view.
}


@end

请添加图片描述
对于打印1,对象还没有初始化 为NULL

对于打印2,对象在test2属性持有了obj1,obj2变量, obj1使用了__autoreleasing

对于打印3, weakObj1和weakObj2都是使用weak修饰符,但是在test2里面对obj1使用了__autoreleasing修饰符,所以obj1会超出自己的作用地域而存在,但是obj2么有使用延迟销毁修饰符。

汇编看看

针对obj1和obj2的初始化

- (void)howDelate {
    __autoreleasing NSObject *obj1 = [NSObject new];
    NSObject *obj2 = [NSObject new];
}

请添加图片描述

- (void)howDelate伪代码{

   // __autoreleasing NSObject *obj1 = [NSObject new]; 等同于:
    id obj1 = objc_msgSend(NSObject, @selector(new));
    objc_autorelease(obj1);

   // NSObject *obj2 = [NSObject new]; 等同于:
    id obj2 = objc_msgSend(NSObject, @selector(new));
    defer {
       objc_storeStrong(&obj2,nil)
    }
}

 void objc_storeStrong(id *location, id obj) {
    id prev = *location;
    if (obj == prev) {
        return;
    }
    objc_retain(obj);
    *location = obj;
    objc_release(prev);
}

obj1 在被__autoreleasing修饰后被加入至自动缓存池,并且在函数结束时不会再调用objc_storeStrong,因此摆脱函数作用域对它的生命周期的影响。

显式和隐式的调用__autoreleasing

其实在上述的obj1 已经显示的调用了__autoreleasing

  __autoreleasing NSObject *obj1 = [NSObject new];

隐式使用__autoreleasing的情况就是你的代码上看不到它,但是它编译后确实会存在,

//这个是我们的TestObj的创建对象的一个自定义类方法
+ (TestObj *)obj
{
  /*
    本来这个对象创建出来默认是__strong的,
    但是因为编译器发现这个对象是要被当做返回值返回出去的,
    所以不能在这个方法结束时候就release这个对象,
    而应该放长远的眼光,使用autorelease,这个其实也是在MRC里
  */
  TestObj * obj = [[TestObj alloc] init];
  return obj;
}

NSError**与autoreleasing的关系

再次回到刚开始的问题,这两个东西看似没什么关系,其实大有联系**。他们俩是绑定关系**

为什么呢?

先介绍NSError**

NSError** 是指向一个 NSError 对象指针的指针,通常用于在方法中传递错误信息。使用这种方式传递错误信息的原因是如果在方法执行过程中发生错误,方法本身可能无法处理该错误,因此需要将错误信息返回给调用方以供处理

什么是指向对象指针的指针?

指向对象指针的指针是指一个指针变量,它存储的是另一个指针变量的地址,而这个指针变量指向的是一个对象请添加图片描述

为什么NSError需要__autoreleasing的帮助?

在方法中将 NSError 对象传递给调用方时,通常需要使用 __autoreleasing 修饰符,以确保错误对象能够被正确地释放。具体来说,如果不使用 __autoreleasing 修饰符,那么在方法返回时,该对象可能被自动释放池回收,导致调用方得到的错误对象是一个悬垂指针,无法正常使用。因此,在将 NSError 对象传递给调用方时,通常使用以下方式定义:

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

这样,调用方传递一个指向 NSError 指针的指针,方法在执行过程中需要修改该指针指向的对象时,使用 *error 进行操作。在方法返回时,自动释放池会自动管理 NSError 对象的释放,无需手动释放。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。