美文网首页iOS开发基础篇#iOS#HeminWoniOS进阶
为什么传递NSError需要指针的指针

为什么传递NSError需要指针的指针

作者: 7hriller | 来源:发表于2016-08-19 23:43 被阅读1173次

在编写Objective-C代码时,很多时候会需要对错误进行处理,在OC里使用的是NSError。当我们编写一个方法时,比如进行一个网络请求,这个时候会有请求成功或请求失败两种情况。当请求失败时,我们会在方法中生成一个错误并告诉调用方。

在C语言里,返回数据有两种方式,一种是常用的return返回,这是大部分情况下从函数返回数据的方式,但C语言里一个函数只能返回一个数据,也就是return后面只能有一个指针或值(包括结构体)。如果想要返回多个数据时,有时会考虑使用参数返回的形式。

最典型的例子就是交换两个数值的函数:

int a = 1, b = 2;
swap(&a, &b);

void swap(int *a, int *b) 
{
   int tmp = *a; *a = *b; *b = tmp;
}

交换外部变量的方式很多,如指针、引用、位运算,但不能直接使用变量,这是因为形参和实参的区别,具体不用多说。此处想强调的是,不使用return语句,返回两个交换后的变量值,可以使用这种参数指针的形式。

铺垫这些,是为了说明NSError的使用情况。在OC中错误生成的常见形式是这样:

-(id)requestWithParameter:(id)obj error:(NSError *__autoreleasing *)error
{
   id result = ... //使用obj参数进行网络请求并返回
   if (result != nil) 
   {
      return result;
   }
   else
   {
      if (error != NULL) //判断调用方是否需要获取错误信息
      {
         *error = [NSError errorWithDomain:...]; //生成错误对象
      }
      return nil;
   }
}

对比发现,交换指针的方法使用的是单指针作为参数,直接交换了指针指向的内容;而错误生成的案例中,错误参数使用的是指针的指针。

实际上,他们的本质是一样的。

我们先考虑这种情况:

-(id)requestWithParameter:(id)obj error:(NSError __autoreleasing *)error
{ 
   id result = ... //使用obj参数进行网络请求并返回 
   if (result != nil) 
   {
      return result; 
   } 
   else 
   { 
      if (error != NULL) //判断调用方是否需要获取错误信息
      { 
         error = [NSError errorWithDomain:...]; //生成错误对象 
      } 
      return nil; 
   }
}

上面的代码会发生什么?如果在上面代码的基础上在外部创建一个错误对象然后调用方法,最后打印:

NSError *error;
[self doSomethingWithObj:nil error:error];
NSLog(@"error: %@", error);

此时打印结果会是什么?运行一下会发现控制台输出:

error: (null)

也就是说方法中创建的新的NSError实例并没有传递给外部的对象指针。其实分析一下可知道,error = [NSError errorWithDomain:...]; //生成错误对象此处只是将新的实例指针分配给了error这个方法内的局部指针变量,而这个局部指针变量是外部指针变量的一个拷贝。

当方法调用结束,外部并没有对这个新实例的强引用,因此也就会被释放掉。同时外部的NSError指针也无法指向这个新的对象。

这就好比那个指针交换数值的例子,将其转换成错误的值传递:

void swap(int a, int b)
{
   int tmp = a; a = b; b = tmp;
}

此处只是对局部变量a, b进行了交换,函数出栈后,这两个局部变量都被释放,而外部的变量值并不改变。

回到NSError,为了能够将方法中创建的NSError实例分配给外部的那个error指针指向的地址,我们需要将外部指针变量存储的地址直接传递给方法进行值拷贝,而不是传递指针变量本身。这是因为如果传递指针变量本身的话,方法只会拷贝一个指针,虽然拷贝后的指针和外部的指针都指向同一个地址,但是指针本身的地址是不同的。

而如果传递&error,即取error指针本身的地址,则是单纯的值拷贝(但实际情况略有区别,稍复杂一些,因为此处增加了__autorelease关键字,将指针对象自动入池,这个过程会对指针地址做一些处理,导致拷贝的地址会有偏移),会保留这个指针的地址并在方法内部恢复指针,同时新建NSError实例并取地址给这个指针

类似的,我们可以写一个交换NSError的方法来跟交换数值的方法进行对比理解:

- (void)swapError:(NSError **)a with:(NSError **)b
{
    NSError *tmp = *a;
    *a = *b;
    *b = tmp;
}

因此,我们可以得出一个结论,就是使用参数传递返回OC指针类型的对象时,指针的指针是一种比较方便的处理参数返回方式。

如有错误望不吝指正!

相关文章

  • 为什么传递NSError需要指针的指针

    在编写Objective-C代码时,很多时候会需要对错误进行处理,在OC里使用的是NSError。当我们编写一个方...

  • 读书笔记17.06.02【stack】【vector】

    C++中参数传递:按值传递,指针传递和引用传递按值传递:形参是实参的拷贝。指针传递:拷贝指针,被调用函数对指针指向...

  • C++基础

    C++ 值传递、指针传递、引用传递详解C++中引用传递与指针传递区别 引用传递和指针传递的区别 引用的规则:(1)...

  • Go语言指针作为函数参数

    Go 语言允许向函数传递指针,只需要在函数定义的参数上设置为指针类型即可。以下实例演示了如何向函数传递指针,并在函...

  • C指针(4):指针传递和返回数据

    指针传递和返回数据 1.交换数据 2.返回指针 3.局部数据指针 4.传递指针的指针 5.自定义free函数

  • 指针

    指针 数组指针和指针数组 函数指针和指针函数 指针作为参数 指针多用于处理值传递,减少值复制耗费的内存

  • 关于指针传递和指针的指针

    一开始没有理解,导致对这个概念非常的模糊和不解。最近看完《 彻底搞定C语言指针详解》,里面关于指针的指针的解释有了...

  • OC中方法实现输出参数的方式

    实现方式如图 NSError 作为输出参数为什么使用双指针的原理: C语言实现输出参数原理一样:

  • C Primer Plus学习笔记03

    指针的操作 注意:指针不可以相加 也不能进行乘除运算 利用指针传递数组 如果传递的数组是允许改变的 直接利用指针传...

  • 为什么OC中传入NSError要传指针的指针

    看到这样的代码时会不会有什么疑问?为什么传的是NSError ** ? 明确一个前提在方法中形参都是实参的值拷贝,...

网友评论

  • 我是繁星:想改变什么值就传入什么值得指针,想改变的是NSError * error所以传入的就是NSError ** error
  • 0395d56c5d1e:赞,解决了疑惑,谢谢分享:smile:
  • NSString:写的不错
  • C_HPY:分析的不错。浅显易懂
  • Zentopia:写这么一大堆,其实一句话就可以解决,方法的参数,传递的是变量的值而不是变量本身。
    KeepMoveingOn:@roylly 外部传入的对象其实为指针变量,也是一种变量。这时候如果需要改变外部指针变量的指向就要传入指针变量的地址,即指针的指针。
    roylly://生成错误对象此处只是将新的实例指针分配给了error这个方法内的局部指针变量,而这个局部指针变量是外部指针变量的一个拷贝。我主要是理解了这句。对于C语言,值传递和地址传递确实理解不是很到位。地址传递以为就没有实参copy到形参了,但实际上也是copy了一个指针,只不过形参和实参指针指向同一个对象而已,所以形参对对象的修改,在函数外部也生效。 而在方法内部生成对象,并通过参数返回,这个就需要指针的指针,即传递指针地址了,而指针地址的传递也是一种值传递。作者觉得我理解得对吗?
    7hriller:@薛丁格的猫 任何文章一句话都可以概括,问题是文章的受众不同,理解的深度就会不同。教授看四则运算也就是一句话,但小学生是不是需要多看几句?我这篇是针对对C语言指针理解不够深入的人群写的。

本文标题:为什么传递NSError需要指针的指针

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