我很高兴找到
Swift 3的#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().
The property name must be a reference to a property that is available in the Objective-C runtime. At compile time,the key-path expression is replaced by a string literal.
可以独立保存这个“属性名称”,然后传递给#keyPath()来创建一个String吗?
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进行交互.我想编写方便的方法,将有效的属性名称作为参数,而不是随机密钥路径字符串.理想情况下,由特定类型实现的属性名称.
func doSomethingForSpecificTypeProperty(_ propertyName: MyObject.PropertyName) { let keyPathString = #keyPath(propertyName) let expression = NSExpression(forKeyPath: keyPathString) // ... }
它看起来不可能.
原文链接:/swift/319051.html这是解析密钥路径表达式的编译器代码:
/// 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,rParenLoc)); }
此代码首先在括号内创建一个以句点分隔的名称列表,然后尝试将它们解析为表达式.它接受表达式而不是任何Swift类型的数据;它接受代码,而不是数据.