Proper Usage of @selector() in Swift and Detailed Explanation of #selector Expression

Nov 21, 2025 · Programming · 11 views · 7.8

Keywords: Swift | Selector | #selector Expression | Objective-C Interoperability | Compiler Checking

Abstract: This article provides an in-depth exploration of handling Objective-C selectors in Swift, focusing on the usage scenarios and advantages of the #selector expression. By comparing traditional string construction methods with modern #selector syntax, it analyzes key concepts such as compiler checking, type safety, and method exposure in detail, offering complete code examples and practical guidance. The article also covers advanced topics including selector availability, parameter handling, and property accessors, helping developers avoid common pitfalls and errors.

Basic Concepts of Selectors in Swift

In the Swift programming language, selectors are an important concept, particularly when interacting with Objective-C APIs. Although Swift's design philosophy favors safer programming patterns such as closures and protocol extensions, many Objective-C-based frameworks (like Foundation and UIKit) still widely use the selector pattern.

Traditional Issues and Error Examples

Many developers encounter selector-related problems when transitioning from Objective-C to Swift. A typical error example is as follows:

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

This code produces a compilation error: "Could not find an overload for 'init' that accepts the supplied arguments". The root cause is that the developer incorrectly passes the function call test() directly to the selector parameter, rather than passing a reference to the function itself.

Correct Usage of the #selector Expression

Starting from Swift 2.2, the #selector expression was introduced, which is the recommended way to handle selectors. The correct usage is as follows:

let timer = Timer(timeInterval: 1, target: self, 
                 selector: #selector(MyClass.test), 
                 userInfo: nil, repeats: false)

The main advantage of the #selector expression is that it provides compile-time checking. The Swift compiler verifies:

Selector Availability Requirements

For a method to be accessible via a selector, the following conditions must be met:

class MyClass: NSObject {
    @objc func test() {
        // Method implementation
    }
}

In Swift 4 and later, the @objc attribute must be explicitly used to expose the method to the Objective-C runtime. Additionally, the method's visibility must be at least internal, as private methods are not recognized by the runtime.

Handling Methods with Parameters

For methods with parameters, the parameter labels must be explicitly specified in the selector:

class ViewController: UIViewController {
    @objc func buttonTapped(sender: UIButton) {
        // Handle button tap
    }
    
    func setupButton() {
        let button = UIButton()
        button.addTarget(self, action: #selector(buttonTapped(sender:)), 
                        for: .touchUpInside)
    }
}

Selectors for Property Accessors

Swift 3.0+ provides special syntax for handling property getters and setters:

class Person: NSObject {
    @objc var name: String = ""
}

// Get selector for property getter
let getNameSelector = #selector(getter: Person.name)

// Get selector for property setter  
let setNameSelector = #selector(setter: Person.name)

Alternative Approach: String-Based Selector Construction

In certain dynamic scenarios, it may be necessary to construct selectors using strings:

let dynamicSelector = Selector("dynamicMethod:")

This approach loses the advantage of compile-time checking but offers greater flexibility. It is important to follow Objective-C naming conventions, including adding colons for each parameter.

Common Pitfalls and Best Practices

In practice, developers need to be aware of the following common issues:

Integration with Modern Swift Features

As Swift evolves, many scenarios that previously required selectors can now be implemented in more modern ways:

// Using closures instead of selectors
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { timer in
    self.handleTimer()
}

This approach provides better type safety and memory management and is the preferred solution in Swift.

Conclusion

The #selector expression is the standard way to handle Objective-C selectors in Swift, combining type safety with runtime flexibility. Developers should prioritize this method and consider string-based selector construction only in dynamic scenarios. Meanwhile, as the Swift language evolves, many traditional selector usage scenarios are being replaced by more modern programming patterns.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.