Swift 3: Why symbol _ is added before sender in pa

2019-06-25 12:27发布

My Xcode has been recently updated to Xcode 8 with Swift 3. What I have noticed is that when attaching actions to ViewController functions are generated in this style:

@IBAction func methodName(_ sender: UIButton) {

}

I don't understand what is the purpose of having _ there. Could anyone explain why it is there? In previous Swift 2.2 it was not there.

Another question is that previous project I have done by watching tutorial has migrated to Swift 3, and there was one method which looked like this:

func toggleButtons(toggle: Bool) {
    yesButton.isUserInteractionEnabled = toggle
    noButton.isUserInteractionEnabled = toggle
}

I was calling this method like this: toggleButtons(false) still not passing the argument name. Now migrator has changed it as below but nothing was changed in calling of method later in code.

func toggleButtons(_ toggle: Bool) {
    yesButton.isUserInteractionEnabled = toggle
    noButton.isUserInteractionEnabled = toggle
}

Is it a different situation from the previous case?

2条回答
对你真心纯属浪费
2楼-- · 2019-06-25 13:07

It means that you do not have to name the parameter when calling the method.

obj.methodName(button)

// no need for
obj.methodName(sender: button)

In earlier Swift versions, that was the special default behaviour for the first parameter in a method call, but in Swift3 that has been unified. All parameters now need names, unless you opt out (with the _).

查看更多
冷血范
3楼-- · 2019-06-25 13:25

The other answers (one of which has been deleted) covered what the _ means: it eliminates the keyword when you call the method.

However, your question is specifically about “attaching actions to ViewController”.

Methods that have the @IBAction attribute can be connected to controls in your storyboard (or xib). That means the name of the method will be stored, as a string, in the storyboard, and it will be called using the Objective-C message sending system. So the exact name, and how the name is translated to Objective-C, matters very much here.

If you declare the method func methodName(sender: UIButton), then its Objective-C name is methodNameWithSender:.

If you declare the method func methodName(_ sender: UIButton), then its Objective-C name is methodName:.

Both iOS and macOS have something called the “responder chain”, which lets the system search for an object (a “responder”) that can handle an action. You can connect the controls in your storyboard to the First Responder (at the top of the document outline) to have the system do this. Most commonly you do this with menu items in macOS, but there are other uses.

Anyway, there are some standard actions like cut:, copy: and paste:. For example, you would set your Cut menu item's action to send cut: to First Responder. And if you want to implement the cut: method in your view controller, it is critical that its name (when translated to Objective-C) be precisely cut:, and not cutWithSender:. If it's named cut:, the system will find it when searching the responder chain, but if it's named cutWithSender:, the system will not find it and you will wonder why your menu item doesn't work.

Xcode knows this, so it always creates action methods of the form @IBAction func action(_ sender: UIButton), omitting the keyword from the argument, in case you are implementing an action that needs to be found by searching the responder chain.

In Swift 3, every argument to a method has a keyword by default, including the first argument. Back in Swift 2, the first argument did not have a keyword by default, so the _ on the first argument was redundant in Swift 2 (and would generate a compiler warning). That is why Xcode 8 (Swift 3) puts in the _ but Xcode 7 (Swift 2.2) did not.

That is also why your toggleButtons example worked in Xcode 7.

查看更多
登录 后发表回答