传给#keyPath()的非字符串"property name"能独立保存吗?
Can the non-string "property name" passed to #keyPath() be saved independently?
我很高兴找到 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 作为 "property name" 传递到 #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.
是否可以单独保存这个 "property name",然后传递给 #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 进行交互。我想编写将有效的 属性 名称作为参数而不是随机键路径字符串的便捷方法。理想情况下,属性 名称由特定类型实现。
func doSomethingForSpecificTypeProperty(_ propertyName: MyObject.PropertyName) {
let keyPathString = #keyPath(propertyName)
let expression = NSExpression(forKeyPath: keyPathString)
// ...
}
看起来不可能。
下面是编译器解析关键路径表达式的代码:
/// 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));
}
此代码首先在括号内创建 period-separated 个名称的列表,然后尝试将它们解析为表达式。它接受表达式而不是任何 Swift 类型的数据;它接受 code,而不是 data。
刚刚提出类似问题并找到 this article。您可以使用 KeyPath 泛型用于这些目的
swift4 中的短代码如下所示:
let getName = \Person.name
print(p[keyPath: getName])
// or just this:
print(p[keyPath: \Person.name])
我很高兴找到 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 作为 "property name" 传递到 #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.
是否可以单独保存这个 "property name",然后传递给 #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 进行交互。我想编写将有效的 属性 名称作为参数而不是随机键路径字符串的便捷方法。理想情况下,属性 名称由特定类型实现。
func doSomethingForSpecificTypeProperty(_ propertyName: MyObject.PropertyName) {
let keyPathString = #keyPath(propertyName)
let expression = NSExpression(forKeyPath: keyPathString)
// ...
}
看起来不可能。
下面是编译器解析关键路径表达式的代码:
/// 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));
}
此代码首先在括号内创建 period-separated 个名称的列表,然后尝试将它们解析为表达式。它接受表达式而不是任何 Swift 类型的数据;它接受 code,而不是 data。
刚刚提出类似问题并找到 this article。您可以使用 KeyPath 泛型用于这些目的
swift4 中的短代码如下所示:
let getName = \Person.name
print(p[keyPath: getName])
// or just this:
print(p[keyPath: \Person.name])