2017-02-08 3 views
6

Swift 3の実装である#keyPath()が見つかりました。これは、誤った入力を排除し、コンパイル時にキーパスが実際に存在するように強制します。手動で文字列を入力するよりもはるかに優れています。 "プロパティ名" として#keyPath()に渡される#keyPath()に渡される文字列以外の「プロパティ名」を個別に保存することはできますか?

https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md

class MyObject { 
    @objc var myString: String = "default" 
} 

// Works great 
let keyPathString = #keyPath(MyObject.myString) 

Swift docs list the type

"property name"

プロパティ名は、Objective-Cランタイムに利用可能であるプロパティへの参照でなければなりません。コンパイル時に、キーパス式は文字列リテラルに置き換えられます。

この「プロパティ名」を個別に保存してから、後で#keyPath()に渡して文字列を作成できますか?

let propertyName = MyObject.myString // error. How do I save? 
let string = #keyPath(propertyName) 

特定のタイプに属するプロパティー名を要求するサポートはありますか?

// something like this 
let typedPropertyName: MyObject.PropertyName = myString // error 
let string = #keyPath(typedPropertyName) 

最終目標は、キーパスのNSExpressionが必要なAPIでと対話します。私は、ランダムなキーパス文字列ではなく、パラメータとして有効なProperty Nameをとる便利なメソッドを記述したいと考えています。理想的には、特定のタイプによって実装されるプロパティ名。

func doSomethingForSpecificTypeProperty(_ propertyName: MyObject.PropertyName) { 

    let keyPathString = #keyPath(propertyName) 

    let expression = NSExpression(forKeyPath: keyPathString) 

    // ... 
} 
+0

スウィフト4の[スマートキーパス:スウィフトのためのコーディングより良いキー値](HTTPS:/あなたはSWIFT 4で、このための短いコードは次のようになります

これらの目的のために、一般的なキーパスを使用することができます/github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md)より良いオプションが追加されているようです。もし誰かがそれに着く前にそれを書いておきたいなら、その答えを受け入れるでしょう。 – pkamb

+0

https://bugs.swift.org/browse/SR-5220 - 「KeyPathの文字列表現を取得するAPIを公開する」 – pkamb

+0

https://github.com/kishikawakatsumi/Kuery - 「タイプセーフなコアデータクエリAPIです。 Swift 4のスマートKeyPaths " – pkamb

答えて

4

これは可能ではないようです。ここで


は、キーパス式を解析するために、コンパイラのコードです:

/// expr-keypath: 
///  '#keyPath' '(' unqualified-name ('.' unqualified-name) * ')' 
/// 
ParserResult<Expr> Parser::parseExprKeyPath() { 
    // Consume '#keyPath'. 
    SourceLoc keywordLoc = consumeToken(tok::pound_keyPath); 

    // Parse the leading '('. 
    if (!Tok.is(tok::l_paren)) { 
    diagnose(Tok, diag::expr_keypath_expected_lparen); 
    return makeParserError(); 
    } 
    SourceLoc lParenLoc = consumeToken(tok::l_paren); 

    // Handle code completion. 
    SmallVector<Identifier, 4> names; 
    SmallVector<SourceLoc, 4> nameLocs; 
    auto handleCodeCompletion = [&](bool hasDot) -> ParserResult<Expr> { 
    ObjCKeyPathExpr *expr = nullptr; 
    if (!names.empty()) { 
     expr = ObjCKeyPathExpr::create(Context, keywordLoc, lParenLoc, names, 
            nameLocs, Tok.getLoc()); 
    } 

    if (CodeCompletion) 
     CodeCompletion->completeExprKeyPath(expr, hasDot); 

    // Eat the code completion token because we handled it. 
    consumeToken(tok::code_complete); 
    return makeParserCodeCompletionResult(expr); 
    }; 

    // Parse the sequence of unqualified-names. 
    ParserStatus status; 
    while (true) { 
    // Handle code completion. 
    if (Tok.is(tok::code_complete)) 
     return handleCodeCompletion(!names.empty()); 

    // Parse the next name. 
    DeclNameLoc nameLoc; 
    bool afterDot = !names.empty(); 
    auto name = parseUnqualifiedDeclName(
        afterDot, nameLoc, 
        diag::expr_keypath_expected_property_or_type); 
    if (!name) { 
     status.setIsParseError(); 
     break; 
    } 

    // Cannot use compound names here. 
    if (name.isCompoundName()) { 
     diagnose(nameLoc.getBaseNameLoc(), diag::expr_keypath_compound_name, 
       name) 
     .fixItReplace(nameLoc.getSourceRange(), name.getBaseName().str()); 
    } 

    // Record the name we parsed. 
    names.push_back(name.getBaseName()); 
    nameLocs.push_back(nameLoc.getBaseNameLoc()); 

    // Handle code completion. 
    if (Tok.is(tok::code_complete)) 
     return handleCodeCompletion(false); 

    // Parse the next period to continue the path. 
    if (consumeIf(tok::period)) 
     continue; 

    break; 
    } 

    // Parse the closing ')'. 
    SourceLoc rParenLoc; 
    if (status.isError()) { 
    skipUntilDeclStmtRBrace(tok::r_paren); 
    if (Tok.is(tok::r_paren)) 
     rParenLoc = consumeToken(); 
    else 
     rParenLoc = PreviousLoc; 
    } else { 
    parseMatchingToken(tok::r_paren, rParenLoc, 
         diag::expr_keypath_expected_rparen, lParenLoc); 
    } 

    // If we cannot build a useful expression, just return an error 
    // expression. 
    if (names.empty() || status.isError()) { 
    return makeParserResult<Expr>(
      new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc))); 
    } 

    // We're done: create the key-path expression. 
    return makeParserResult<Expr>(
      ObjCKeyPathExpr::create(Context, keywordLoc, lParenLoc, names, 
            nameLocs, rParenLoc)); 
} 

このコードは最初の括弧内の期間で区切られた名前のリストを作成し、それが表現としてそれらを解析しようとします。これは、スウィフトタイプのデータではなく、式を受け入れます。それはコードを受け取り、データではありません。

+0

これは、Swiftの将来のバージョンで導入されることをうまくいけばよい! – pkamb

1

同様の質問が出て、this articleが見つかりました。

let getName = \Person.name 
print(p[keyPath: getName]) 

// or just this: 
print(p[keyPath: \Person.name]) 
関連する問題