2017-02-08 31 views
6

Swift 3'ün #keyPath() uygulamasını bulmasından çok memnun oldum, bu da yazım hatalarını ortadan kaldıracak ve anahtar yolunun gerçekte var olduğu derleme zamanında uygulanacak. El ile dizeleri yazmaktan çok daha iyi. #keyPath() öğesine iletilen dize olmayan "özellik adı" bağımsız olarak kaydedilebilir mi?

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

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

Swift docs list the type

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

bir "özellik adı" olarak #keyPath() teslim ediliyorlar.

"property name"

özelliği adı

Amaç-C çalışma zamanı mevcut olan bir özellik, bir referans olmalıdır. Derleme zamanında, anahtar yol ifadesi bir dizgi değişmeziyle değiştirilir.

Bu "özellik adı" nı bağımsız olarak kaydetmek mümkün mü, sonra bir String oluşturmak için #keyPath() numarasına geçmek mümkün mü?

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

Belirli bir Tür'e ait bir özellik adı gerektiren herhangi bir destek var mı?

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

nihai hedef anahtar yolu için bir NSExpression gerektiren API'larla ile etkileşim olacaktır. Rasgele anahtar yol dizeleri yerine, geçerli bir Özellik Adını parametre olarak alan kolaylık yöntemleri yazmak istiyorum. İdeal olarak, belirli bir Tür tarafından uygulanan bir Özellik Adı.

func doSomethingForSpecificTypeProperty(_ propertyName: MyObject.PropertyName) { 

    let keyPathString = #keyPath(propertyName) 

    let expression = NSExpression(forKeyPath: keyPathString) 

    // ... 
} 
+0

Swift 4'ün: Bu amaçlarla

hızlı 4'te bunun için kısa kod şuna benzer için jenerik Ana yol kullanabilirsiniz [Akıllı KeyPaths: Daha iyi Anahtar Değer Swift için Kodlama]:/(https /github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md) daha iyi bir seçenek ekliyor gibi görünüyor. Bunu cevaplamadan önce yazmak istediğinde cevabı kabul eder. – pkamb

+0

https://bugs.swift.org/browse/SR-5220 - "KeyPath'in dize gösterimini almak için API'yi aç" – pkamb

+0

https://github.com/kishikawakatsumi/Kuery - "Kullanarak güvenli tipte bir Çekirdek Veri sorgusu API'sı Swift 4'ün Akıllı KeyPaths " – pkamb

cevap

4

Mümkün gibi görünmüyor. İşte


bir anahtar yolu ifadesini ayrıştırmak için derleyici'nın kod:

/// 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)); 
} 

Bu kod ilk olarak parantez içinde noktayla ayrılan isim listesi oluşturur ve daha sonra bir ifadesi olarak bunları ayrıştırmak için çalışır . Herhangi bir Swift türünün bir ifadesini değil, bir ifadeyi kabul eder; kodu, veri verilerini kabul eder.

+0

Umarım bu Swift'in gelecekteki bir sürümünde tanıtılacak! – pkamb

1

Sadece benzer bir soru ile geldi ve bulundu this article.

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

// or just this: 
print(p[keyPath: \Person.name])