2017-02-08 7 views
6

Я был очень рад найти реализацию Swift 3 от #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 передается в #keyPath() как "имя свойства".

"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) 

Конечная цель будет взаимодействовать с с API, которые требуют NSExpression для ключевого пути. Я хотел бы написать удобные методы, которые принимают правильное имя свойства как параметр, а не случайные строки пути ключа. В идеале, имя свойства, реализованное определенным типом.

func doSomethingForSpecificTypeProperty(_ propertyName: MyObject.PropertyName) { 

    let keyPathString = #keyPath(propertyName) 

    let expression = NSExpression(forKeyPath: keyPathString) 

    // ... 
} 
+0

Swift 4 в [Смарт KeyPaths: кодирование Better Key-Value для Swift] (HTTPS://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md), кажется, добавляет лучший вариант. Примите этот ответ, если кто-то захочет написать его до того, как я доберусь до него. – pkamb

+0

https://bugs.swift.org/browse/SR-5220 - «Expose API для извлечения строкового представления KeyPath» – pkamb

+0

https://github.com/kishikawakatsumi/Kuery - «API-интерфейс запроса на основные типы данных, использующий Swift 4's Smart 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)); 
} 

Этот код сначала создает список имен периодов, разделенных внутри скобок, а затем он пытается разобрать их как выражение , Он принимает выражение, а не данные любого типа Swift; он принимает код, а не данные.

+0

Надеюсь, это будет представлено в будущей версии Swift! – pkamb

1

Только что придумал аналогичный вопрос и нашел this article. Вы можете использовать ключевого ресурса родовое для этих целей

Короткий код для этого в быстром 4 выглядит следующим образом:

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

// or just this: 
print(p[keyPath: \Person.name]) 
Смежные вопросы