声明方法参数__autoreleasing的好处是什么? [英] What are the advantages of declaring method arguments __autoreleasing?

查看:131
本文介绍了声明方法参数__autoreleasing的好处是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

按照过渡到ARC发行说明:

__ autoreleasing用于表示通过引用(id *)传递并在返回时自动释放的参数.

例如:

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

但是与上述相比,上述优点是什么?

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


更新:

几个答案是关于temp var技巧的编译器所做的,它们将var和参数之间的不匹配视为__autoreleasing advantage .我不明白为什么编译器不能对__strong参数执行相同的技巧.我的意思是,对于__weak var和__strong参数,编译器可以类似地执行此操作:

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

编译器知道-(BOOL)performOperationWithError:(NSError * __strong *)error;返回一个强引用(+1),因此它像处理任何new -family方法一样对其进行处理.由于tmperror在同一范围内,因此只要error,编译器就可以合理地使它保持活动状态,因此__strong引用(tmp)现在支持__weak引用(error). ),并且在范围结束之前不会被取消.

解决方案

tl; dr

在这种情况下,将__weak对象转换为__strong对象将改变程序的语义,这是编译器永远都不会做的.


场景

让我们举个例子

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

在这种情况下,error局部变量由ARC自动推断为__strong.

同时error自变量

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

的类型为NSError * __autoreleasing *.

请注意,在任何情况下,ARC都会将引用(id *)传递的参数推断为id __autoreleasing *类型,因此上述签名等同于

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

在ARC下.

因此我们有一个不匹配的地方,因为我们将带__strong注释的变量传递给需要__autoreleasing自变量的方法.

引擎盖下

在我们的示例中,编译器将通过创建本地__autoreleasing tmp变量来解决此类不匹配问题.

代码变为

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

替代

现在我们假装我们可以更改performOperationWithError:的签名.

如果我们想避免编译技巧",使用tmp变量,我们可以将签名声明为

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

我们有一个__strong变量,现在我们将其传递给需要__strong自变量的方法,因此我们消除了不匹配.

看起来不错,为什么不总是声明__strong自变量?

一个原因是,将参数声明为__autoreleasing将使该方法甚至接受__weak引用.

在当前示例中这没有多大意义,但是在某些情况下,我们希望通过引用传递__weak变量并声明__autoreleasing(或让ARC进行推断)将允许我们这样做.

ARC将应用与上述相同的技巧,创建一个__autoreleasing tmp变量.

结论

到目前为止,该机制的名称为 pass-by-writeback .

已设计这种机制与__autoreleasing__strong__weak变量一起使用,以便程序员可以安全地依赖编译器进行的类型推断,而不必太在意周围的变量注释./p>

在某些情况下声明id __strong *自变量可能是有道理的,但通常可能导致编译器生成意外错误.

我在这里的建议是:让编译器发挥他的魔力,你会很好的"

更新

我不明白为什么编译器不能对__strong参数做同样的事情.

告诉编译器以__autoreleasing方式处理__strong__weak变量的管理是可以的,因为它的基本意思是:请编译器,自动执行正确的操作".

这就是为什么上面看到的技巧可以毫无问题地起作用的原因.

另一方面,如果将变量声明为__weak,则可能有充分的理由,而最后要做的就是在明确指定时隐式保留该变量.那会从根本上改变您编写的代码的语义,因此编译器不会这样做(谢谢上帝!).

换句话说

__weak-> __autoreleasing
__strong-> __autoreleasing
__weak<-> __strong 错!

As per Transitioning to ARC Release Notes:

__autoreleasing is used to denote arguments that are passed by reference (id *) and are autoreleased on return.

For example:

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

But what are the advantages of the above comparing to:

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


Update:

Several answers refer to the temp var trick compiler does to deal with the mismatch between var and argument as the advantage of __autoreleasing. I don't see why compiler can not do the same trick for __strong argument. I mean, for a __weak var and __strong argument, compiler can similarly do this:

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

Compiler knows -(BOOL)performOperationWithError:(NSError * __strong *)error; returns a strong reference(+1) so it handles it just like any new-family method. Since tmp lives in the same scope as error, compiler can reasonably keep it alive as long as error so the __weak reference(error) is now supported by a __strong reference(tmp) and will not be nullified until the end of the scope.

解决方案

tl;dr

Implicitly converting a __weak object to a __strong object in this case would alter the semantic of the program, something that a compiler should never do.


The scenario

Let's take an example

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

In such a case the error local variable is automatically inferred by ARC as __strong.

At the same time the error argument of

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

is of type NSError * __autoreleasing *.

Please note that in any case ARC will infer parameters passed by reference (id *) as being of type id __autoreleasing *, so the above signature is equivalent to

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

under ARC.

Therefore we have a mismatch since we are passing a __strong annotated variable to a method expecting an __autoreleasing argument.

Under the hood

In our example then the compiler will address such mismatch by creating a local __autoreleasing tmp variable.

The code becomes

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

An alternative

Let's now pretend that we can change the signature of performOperationWithError:.

If we want to avoid the "compiler trick" which uses the tmp variable, we can declare our signature as

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

We have a __strong variable and we are now passing it to a method expecting a __strong argument, so we just eliminated the mismatch.

Looks good, why not always declare __strong arguments?

One reason is that declaring the argument as __autoreleasing will make the method to accept even a __weak reference.

It does not make much sense in the current example, but there could be cases in which we'd like to pass a __weak variable by reference and declaring __autoreleasing (or leaving the ARC to infer it) will allow us to do so.

ARC will apply the same trick seen above, creating a __autoreleasing tmp variable.

Conclusion

The mechanism presented so far goes under the name of pass-by-writeback.

Such mechanism has been designed to work with __autoreleasing, __strong and __weak variables, so that the programmer can safely rely on the type inference made by the compiler and not care too much about annotating variables around.

Declaring a id __strong * argument may make sense in some cases, but in general it could lead to unexpected errors generated by the compiler.

My advice here is: "let the compiler do his magic and you'll be good"

Update

I don't see why compiler can not do the same trick for __strong argument.

Telling the compiler to handle in an __autoreleasing fashion the management of either a __strong or __weak variable it's ok since it basically means: "Please, compiler, do the right thing automatically".

That's why the trick seen above will work without issues.

On the other hand, if you declare a variable as __weak you presumably have a good reason for doing so and the last thing you want is to have it implicitly retained when you clearly specified otherwise. That would radically change the semantic of the piece of code you've written, therefore the compiler won't do that (thank God!).

In other words

__weak --> __autoreleasing good
__strong --> __autoreleasing good
__weak <--> __strong wrong!

这篇关于声明方法参数__autoreleasing的好处是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆