将Swift变量名转换为字符串,以便用作字符串?

3
我需要导入一种名字与代表其状态的布尔变量相同的纹理。
let myButtonNameABC = false

有没有办法将此变量的名称转换为字符串,以便可以使用它来加载同名的图像?

我认为你的答案在这里:链接 - Ray Toal
1
@RayToal 看起来就是这样了... NSPredicate(format: "%K == %@", #keyPath(Person.firstName), "Andrew") 但是在这个例子中变量名在哪里,它被发送和存储在哪里呢? - Confused
3
我其实不确定,因为我只写纯Swift代码,没有使用Objective-C交互。如果您可以与keyPath交互,那太好了,也许其他人会在这里回答。但是我想知道,不在Swift中完成这种操作是否是更好的答案。与其创建一个真正关心名称的变量,请考虑使用字典,在其中可以更轻松地访问键名,而不会用Objective-C污染代码?(只是一种思路...) - Ray Toal
这将是一个有趣的提议。如果您有任何想分享的内容,请在swift-evolution上开始一次讨论,请求此功能。 - CodaFi
你的问题的答案在这里: https://stackoverflow.com/a/45622489/1465582 - Irshad Mohamed
2个回答

3
您可以将其制作成一个函数而不是一个变量,并执行相同的操作。我将分步解释。
首先,我们如何使函数与变量相同?
基本上,我们在函数内部“包裹”变量。这类似于我在您最新的RefBool线程中建议的第一个块,我们通过函数获取或设置值。
在这里,我们使用方法/函数直接更改变量而不使用参数,因此,在整个代码中访问它时,我们将使用函数名称而不是变量名称来使用它:
enum Static {
  private static var _button1 = false
  static func button1() { toggle(&_button1) }
}

注意,枚举只是一个命名空间,方便我们调用方法并强调变量名称对于代码工作来说不是必需的--只有函数需要知道它的名称。这段代码同样适用于结构体、类甚至全局命名空间。

因此,在这里,如果我想要改变_button1的值,我必须调用button1() _button1实际上对所有人和一切都是不可见的--这没关系,因为我们只需要button1()来完成我们的工作。

所以现在,每当我们调用Static.button1()时,Static._button1将会使用之前给你的函数在truefalse之间切换。


我们为什么要这样做呢?

因为没有办法在不使用Mirror和反射的情况下获取变量的名称,而且已经有一个名为#function的内置命令。我们通过使用函数来跳过设置镜像和其他OBJc难点的步骤。

让我们修改我们的enum来演示如何获取func的名称:

 enum Static {
  private static var _button1 = false
  
  static func button1() -> String { 
    toggle ( &_button1 ) 
    return ( #function )
  }
}

现在,当我们调用 button1() 函数时,会发生两件事情:1. 切换按钮(_button1)的状态;2. 返回一个名为 "button1()" 的字符串。

let buttonName = Static.button1()  // buttonName = "button1()"

我们可以通过调用String的成员来使其更易于使用并改变自身:
let trimmedButtonName = Static.button1().replacingOccurrences(of: "()", with: "")
// trimmedButtonName = "button1"

现在让我们使用这个方便的替换来更新我们的枚举
enum Static {
  private static var _button1 = false
  
  static func button1() -> String { 
    toggle ( &_button1 ) 
    return ( #function.replacingOccurrences(of: "()", with: "") )
  }
}

我们的最终用例如下所示:
let buttonName = Static.button1()

注意:

您不必将_button1设置为私有的,这只是为了说明您不需要将其设置为公共的。如果您需要获取它的状态,您可以创建另一个函数或从button1()返回一个元组类型:

static func button1() -> (name: String, state: Bool) {
// ...        
  return ( #function.etc, _button1)
}

let results = Static.button1()
print( results.name  ) // "button1"
print( results.state ) //  true / false

如果您需要更加明确地控制变量的设置,可以在参数中设置一些内容,就像任何普通函数一样。


更新:

例如,如果您想要使用明确的控制方式,可以这样做:

func button1(equals: Bool? = nil) -> (name: String, state: Bool) {
  if equals != nil {
    // do the code mentioned above
  } else {
    _button1 = equals!
  }

  return (#function.replacingOccurrences(of: "()", with: ""), _button1)
}

let buttonState.state = Static.button1(equals: false) // false
let buttonStateAgain.state = Static.button1(equals: true) // true
print(Static.button1().name) // "button1"
  

1
这仍然没有解决局部变量的问题。此外,对于应该是免费的东西来说,这是很多样板文件。 - CodaFi
1
@CodaFi请分享您的替代方案。 - Fluidity
你觉得Swift进化提案怎么样? - CodaFi
@CodaFi,这很酷,但你批评我的解决方法,因为它应该是免费的,但它不是。我不明白。 - Fluidity
我对此持批评态度,因为这确实是一种语言限制,而且我们也意识到了这一点。变量的字符串化是任何半靠谱的预处理器都能够胜任的事情;这是可以在编译器中轻松实现的。简而言之,样板代码就是样板代码,但这需要一个更高级和更通用的解决方案。 - CodaFi
显示剩余2条评论

3
您可以使用 Mirror 进行此操作。
struct Test {
    let myButtonNameABC = false
}

let childrens = Mirror(reflecting: Test()).children

childrens.forEach {
    print($0.label ?? "")
}

这将打印出:
myButtonNameABC

你可以在这里找到关于Mirror的信息。


2
你需要设置很多模式匹配才能找到你想要的变量,是吗?还是说要为每个变量创建一个新的结构体? - Fluidity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接