问题
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)'"
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)'"
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)
In Objective-C I was using pointers, transferring the approach to Swift also doesn't work, same result:
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!
回答1:
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:
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.
回答3:
-Use Any instead of AnyObject to cover value types and structs.
-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:
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:
- Use Method overloading (which can duplicate your code and make it congenitally harder to read, even if you refactor)
- Not use inout (this is what you should do)
- Or you can do this:
-
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)
回答5:
The type of object should match the type of parameter, including 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")
来源:https://stackoverflow.com/questions/29963848/swift-ios-how-to-use-inout-parameters-in-functions-with-anyobject-any-or-pointe