Swift/iOS:如何在带有AnyObject/Any或指针的函数中使用inout参数

时间:2021-12-05 18:00:35

I'm trying to write a function that takes a variable pointer and a descriptor/key and sets a new value for the variable. Ideally the pointer should be either of an object or a primitive, but I could also live with separate functions (or an additional parameter). In my code I retrieve the new value from a database also using the key, but in the following example I simplified it with dummy values so that it can be used easily in a playground:

我正在编写一个函数,它接受一个变量指针和一个描述符/键,并为变量设置一个新值。理想情况下,指针应该是对象或原语,但我也可以使用单独的函数(或附加的参数)。在我的代码中,我也使用密钥从数据库中检索新值,但是在下面的示例中,我使用假值对其进行了简化,以便在操场上轻松使用:

import UIKit

func setValue(inout object: AnyObject, key: String) {
    switch key {
    case "String":
        object = "A String"
    case "UIColor":
        object = UIColor.whiteColor()
    case "Bool":
        object = true
    default:
        println("Unhandled key: \(key)")
    }
}

var string: String = "Default String"
var color: UIColor = UIColor.blackColor()
var bool: Bool = false

setValue(&string, "String")
setValue(&color, "UIColor")
setValue(&bool, "Bool")

I get the following error:

我得到以下错误:

"Cannot invoke 'setValue' with an argument list of type '(inout String, key String)'"

"不能用类型的参数列表调用'setValue' (inout String, key String)'"

I understand that I'm mixing Objects and Primitives here. I also tried to break it down and separate these into two functions, but even that fails:

我知道我在这里混合了对象和原语。我也试着把它分解成两个函数,但即使这样也不行

func setValue(inout object: AnyObject, key: String) {
    switch key {
    case "UIColor":
        object = UIColor.whiteColor()
    default:
        println("Unhandled key: \(key)")
    }
}

var color: UIColor = UIColor.blackColor()
setValue(&color, "UIColor")

This also gives the same error:

这也会产生相同的错误:

"Cannot invoke 'setValue' with an argument list of type '(inout UIColor, key String)'"

"不能用类型的参数列表调用'setValue' (inout UIColor, key String) "

If I change 'AnyObject' to 'UIColor' it works, but the point of the function is, that it takes any variable type or at least any object type (I'd write a second function using "Any" for primitives then or add another parameter)

如果我将'AnyObject'改为'UIColor'它是有效的,但函数的要点是,它接受任何变量类型或至少任何对象类型(我将使用“any”为原语编写第二个函数,然后添加另一个参数)

In Objective-C I was using pointers, transferring the approach to Swift also doesn't work, same result:

在Objective-C中,我使用指针,将方法转移到Swift也不起作用,同样的结果是:

func setValue(object: UnsafeMutablePointer<AnyObject>, key: String) {
    switch key {
    case "String":
        object.memory = "A String"
    case "UIColor":
        object.memory = UIColor.whiteColor()
    case "Bool":
        object.memory = true
    default:
        println("Unhandled key: \(key)")
    }
}

Does anybody have an idea what I'm missing here? Any help is much appreciated!

有人知道我漏掉了什么吗?非常感谢您的帮助!

Thanks!

谢谢!

5 个解决方案

#1


8  

Better you can create a generic method like below:

您可以更好地创建如下的泛型方法:

func setValue<T>(inout object:T, key: String) {
        switch key {
        case "String":
            object = ("A String" as? T)!
        case "UIColor":
            object = (UIColor.whiteColor() as? T)!
        case "Bool":
            object = (true as? T)!
        default:
            println("Unhandled key: \(key)")
        }
    }

And calling will be like this:

打电话会是这样的:

setValue(&string, key: "String")
setValue(&color, key: "UIColor")
setValue(&bool, key: "Bool")

Hope it helps!

希望它可以帮助!

#2


7  

The right way to do this is to use overloading, and letting the compiler choose the appropriate bit of code at compile time, instead of switching off a string at runtime:

正确的做法是使用重载,让编译器在编译时选择适当的代码位,而不是在运行时关闭字符串:

func setValue(inout object: String) {
    object = "A String"
}

func setValue(inout object: UIColor) {
    object = UIColor.whiteColor()
}

func setValue(inout object: Bool) {
    object = true
}

func setValue(inout object: Any) {
    println("Unhandled key: \(key)")
}

This approach wouldn’t work when you have an Any and you want to indicate to the function what type is contained in the Any… but in this case, the reason you have problems is that the compiler does know what the types are, so you can take advantage of that.

当你有一个Any的时候,这种方法是行不通的,你想要告诉函数Any中包含了什么类型,但是在这种情况下,你遇到问题的原因是编译器知道类型是什么,所以你可以利用它。

#3


0  

-Use Any instead of AnyObject to cover value types and structs.

-使用Any而不是AnyObject来覆盖值类型和结构。

-You may need to cast to Any before calling your method.

-在调用方法之前,您可能需要对任何方法进行强制转换。

Example:

例子:

func swapAny( inout obj1: Any, inout obj2: Any )
{
    let temp = obj1
    obj1 = obj2
    obj2 = temp
}

use:

使用:

var fooString: Any = "Foo"
var barString: Any = "Bar"

swapAny(&fooString, obj2: &barString)
println(fooString)//prints "Bar"

#4


0  

Protocol support for inout

When you cast a class to a protocol you end up with an immutable reference, which cant be used in inout function parameters. So you can:

当您将类强制转换为协议时,您将得到一个不可变引用,这在inout函数参数中不能使用。所以你可以:

  1. Use Method overloading (which can duplicate your code and make it congenitally harder to read, even if you refactor)
  2. 使用方法重载(它可以复制您的代码,并使其更难于阅读,即使您重构)
  3. Not use inout (this is what you should do)
  4. 不使用inout(这是您应该做的)
  5. Or you can do this:
  6. 或者你可以这样做:

-

- - - - - -

protocol IPositional{
    func setPosition(position:CGPoint)
}
extension IPositional{
    var positional:IPositional {get{return self as IPositional}set{}}
}
class A:IPositional{
    var position:CGPoint = CGPoint()
    func setPosition(position:CGPoint){
        self.position = position
    }
}
func test(inout positional:IPositional){
    positional.setPosition(CGPointMake(10,10))
}
var a = A()
test(&a.positional)
a.position//output: (10.0, 10.0)

Conclusion:
The benefit of doing it this way: Is that you can now have one "inout method" for all classes that implements IPositional But I recommend going with option 2. (Not using inout)

结论:这样做的好处是:对于实现iposition的所有类,现在都可以有一个“inout方法”,但我建议使用选项2。(不使用inout)

#5


0  

The type of object should match the type of parameter, including AnyObject:

对象的类型应该匹配参数的类型,包括AnyObject:

func setValue(inout object: AnyObject, key: String) {
    switch key {
    case "String":
        object = "A String"
    case "UIColor":
        object = UIColor.whiteColor()
    case "Bool":
        object = true
    default:
        print("Unhandled key: \(key)")
    }
}

var string: AnyObject = "Default String"
var color: AnyObject = UIColor.blackColor()
var bool: AnyObject = false

setValue(&string, key: "String")
setValue(&color, key: "UIColor")
setValue(&bool, key: "Bool")

#1


8  

Better you can create a generic method like below:

您可以更好地创建如下的泛型方法:

func setValue<T>(inout object:T, key: String) {
        switch key {
        case "String":
            object = ("A String" as? T)!
        case "UIColor":
            object = (UIColor.whiteColor() as? T)!
        case "Bool":
            object = (true as? T)!
        default:
            println("Unhandled key: \(key)")
        }
    }

And calling will be like this:

打电话会是这样的:

setValue(&string, key: "String")
setValue(&color, key: "UIColor")
setValue(&bool, key: "Bool")

Hope it helps!

希望它可以帮助!

#2


7  

The right way to do this is to use overloading, and letting the compiler choose the appropriate bit of code at compile time, instead of switching off a string at runtime:

正确的做法是使用重载,让编译器在编译时选择适当的代码位,而不是在运行时关闭字符串:

func setValue(inout object: String) {
    object = "A String"
}

func setValue(inout object: UIColor) {
    object = UIColor.whiteColor()
}

func setValue(inout object: Bool) {
    object = true
}

func setValue(inout object: Any) {
    println("Unhandled key: \(key)")
}

This approach wouldn’t work when you have an Any and you want to indicate to the function what type is contained in the Any… but in this case, the reason you have problems is that the compiler does know what the types are, so you can take advantage of that.

当你有一个Any的时候,这种方法是行不通的,你想要告诉函数Any中包含了什么类型,但是在这种情况下,你遇到问题的原因是编译器知道类型是什么,所以你可以利用它。

#3


0  

-Use Any instead of AnyObject to cover value types and structs.

-使用Any而不是AnyObject来覆盖值类型和结构。

-You may need to cast to Any before calling your method.

-在调用方法之前,您可能需要对任何方法进行强制转换。

Example:

例子:

func swapAny( inout obj1: Any, inout obj2: Any )
{
    let temp = obj1
    obj1 = obj2
    obj2 = temp
}

use:

使用:

var fooString: Any = "Foo"
var barString: Any = "Bar"

swapAny(&fooString, obj2: &barString)
println(fooString)//prints "Bar"

#4


0  

Protocol support for inout

When you cast a class to a protocol you end up with an immutable reference, which cant be used in inout function parameters. So you can:

当您将类强制转换为协议时,您将得到一个不可变引用,这在inout函数参数中不能使用。所以你可以:

  1. Use Method overloading (which can duplicate your code and make it congenitally harder to read, even if you refactor)
  2. 使用方法重载(它可以复制您的代码,并使其更难于阅读,即使您重构)
  3. Not use inout (this is what you should do)
  4. 不使用inout(这是您应该做的)
  5. Or you can do this:
  6. 或者你可以这样做:

-

- - - - - -

protocol IPositional{
    func setPosition(position:CGPoint)
}
extension IPositional{
    var positional:IPositional {get{return self as IPositional}set{}}
}
class A:IPositional{
    var position:CGPoint = CGPoint()
    func setPosition(position:CGPoint){
        self.position = position
    }
}
func test(inout positional:IPositional){
    positional.setPosition(CGPointMake(10,10))
}
var a = A()
test(&a.positional)
a.position//output: (10.0, 10.0)

Conclusion:
The benefit of doing it this way: Is that you can now have one "inout method" for all classes that implements IPositional But I recommend going with option 2. (Not using inout)

结论:这样做的好处是:对于实现iposition的所有类,现在都可以有一个“inout方法”,但我建议使用选项2。(不使用inout)

#5


0  

The type of object should match the type of parameter, including AnyObject:

对象的类型应该匹配参数的类型,包括AnyObject:

func setValue(inout object: AnyObject, key: String) {
    switch key {
    case "String":
        object = "A String"
    case "UIColor":
        object = UIColor.whiteColor()
    case "Bool":
        object = true
    default:
        print("Unhandled key: \(key)")
    }
}

var string: AnyObject = "Default String"
var color: AnyObject = UIColor.blackColor()
var bool: AnyObject = false

setValue(&string, key: "String")
setValue(&color, key: "UIColor")
setValue(&bool, key: "Bool")