8145


OS XにおいてObjective-CのコードをSwiftに書き換えています。

[code]

    - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
    {
        SEL action = menuItem.action;
    
        if(action == @selector(reloadContent:)) {
            return YES;
        }
        return NO;
    }
    
[/code]

上記の-validateMenuItem:を書き換えようと思ったのですがSELの比較をSwiftでどのように書けばよいのか分かりません。  
Swiftではどのように書けばよいのでしょうか？


CC BY-SA 3.0
2015-03-19T12:47:45.137
8136
2015-03-22T06:40:38.213
2015-03-22T05:43:11.543
2741
2741
question
objective-c swift macos
SwiftにおけるSelector(SEL)の比較


文字列と比較します。

[code]

    let action:Selector = menuItem.action
    
    if action == "reloadContent:" {
        return true
    }
    
[/code]


CC BY-SA 3.0
2015-03-19T13:00:08.467
8138
2015-03-19T13:00:08.467
3639
8136
answer


Swiftでは、

[code]

    "reloadContent:"
    
[/code]

のような文字列リテラルが`SEL`として扱われます。

このことから、「文字列＝セレクタ」と誤解する向きがありますが、これはコンパイラによる特別扱いで、実際には文字列リテラルから`Selector`構造体が作られています。

`Selector`構造体の定義を読めば分かりますが、`StringLiteralConvertible`プロトコルに準拠しています。ですので、文字列リテラルと比較することは可能です。

ただし、

[code]

    if menuItem.action == "reloadContent:" {
        return true
    }
    
[/code]

のような処理で、`StringLiteralConvertible`としての比較が優先されるのか、`"reloadContent:"`がコンパイラに特別扱いされ`Selector`型同士で同値比較されているのかは、実装次第なので外部から判断が付かない気がします。

その他、移植されたObjective-Cランタイム関数を用いて、

[code]

    sel_isEqual(menuItem.action, "reloadContent:" )
    
[/code]

で比較することもできます。

* * *

**EDIT:**

本題から外れますが、@Harawo
さんの回答の「文字列定数は、自動でSelectorに変換される」という解釈は誤りですので、それを説明するPlayground上で実行できる、簡単なコードを掲載しておきます。（実行環境はXCode
6.3beta、Swift1.2です）

[code]

    let aSelector : Selector = "action:"    //型宣言したセレクタ
    let aConstant : String = "action:"      //文字列定数
    var aString = "action:"                 //型推論させる
    
    
    aSelector == "action:"  // true
    aConstant == "action:"  // true
    
    aSelector == aConstant  // compilation error
    aSelector == aString    // compilation error!!
    
[/code]

文字列定数、というと一般には`let`で宣言した`String`をイメージしますので、その前提で話をしますが、
**Swiftには型の暗黙変換が一切存在しない** ので、文字列定数と`Selector`構造体を比較した場合、`Binary operator '=='
cannot be applied to operands of type 'Selector' and
'String'`というコンパイルエラーになります。

あくまで比較できるのは **文字列リテラル** （リテラルとは、ソースコード中に直接記述されているもの）だけです。文字列、文字列定数とは比較できません。

余談として、型推論させた場合でも`String`として解釈されてエラーになります。

一方で、「Swiftには型の暗黙変換が存在しない」という前言と矛盾しますが、Objective-
Cとの互換のために、`Selector`構造体はC文字列（オペイクポインタである`SEL`の実体）との間で相互に変換することができます。


CC BY-SA 3.0
2015-03-19T14:17:24.840
8145
2015-03-22T06:40:38.213
2015-03-22T06:40:38.213
5337
5337
8136
answer


[iOS Developer Library  
Using Swift with Cocoa and Objective-C  
Interacting with Objective-C
APIs](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-
CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_25)

この中の項「Objective-C Selectors」から引用します。

> An Objective-C selector is a type that refers to the name of an  
>  Objective-C method. In Swift, Objective-C selectors are represented by  
>  the Selector structure. You can construct a selector with a string  
>  literal, such as let mySelector: Selector = "tappedButton:". Because  
>  string literals can be automatically converted to selectors, you can  
>  pass a string literal to any method that accepts a selector.

文字列定数は、自動でSelectorに変換されると、Appleが公式文書で述べているのですから、そのとおり

[code]

    menuItem.action == "reloadContent:"
    
[/code]

で、なんらかまわないと思いますよ。考えすぎて、得なことはありません。


CC BY-SA 3.0
2015-03-20T23:09:48.357
8206
2015-03-20T23:09:48.357
7362
8136
answer