如何根据 NSString 类型键测试属性是否存在和类型?

发布于 2024-10-16 16:45:02 字数 1891 浏览 7 评论 0原文

在我寻求更新 iOS 项目中的核心数据模型时,我在服务器上查询 JSON 对象,这些对象在某种程度上与我的模型的托管实体相对应。我追求的最终结果是从 JSON 输出获得可靠的更新解决方案。

对于本问题中的示例,我将命名核心数据管理对象 existingObj 和传入的 JSON 反序列化字典 updateDict。棘手的部分是处理这些事实:

  1. 并非 existingObj 的所有属性都存在于 updateDict 中,
  2. 并非 updateDict 的所有属性都可用existingObj
  3. 并非所有类型的 existingObj 属性都与 JSON 反序列化属性匹配。 (某些字符串可能需要自定义 Objective-C 包装器)。
  4. updateDict 可能包含 existingObj 中未初始化 (nil) 的键值。

这意味着在迭代更新的字典时,必须来回进行一些属性测试。首先,我必须测试 updateDict 的属性是否存在于 existingObj 中,然后我使用 KVC 设置值,如下所示:

// key is an NSString, e.g. @"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
    [existingObj setValue:[updateDict objectForKey:key] forKey:key];
}

虽然这部分有效,但我不喜欢事实上,我实际上正在测试 displayName 作为 getter,而我即将调用 setDisplayName: setter(间接通过 KVC)。我宁愿是类似 [existingObj hasWritablePropertyWithName:key] 的东西,但我找不到这样做的东西。

这构成了子问题 A:如果只有属性的名称,如何测试属性设置器?

下一部分是我想根据属性类型自动进行属性识别。如果 updateDict 和 existingObj 都有一个 NSString 键 @"displayName",则设置新值很容易。但是,如果 updateDict 包含键 @"color" 的 NSString,即 @"niceShadeOfGreen",我想将其转换为正确的 UIColor 实例。但是如何测试 existingObj 中接收属性的类型,以便我知道何时转换值以及何时简单分配?我希望得到类似 typeOfSelector: 的东西,

if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
     // regular assignment
} else {
     // perform custom assignment
}

当然这是假代码。我不能依赖于测试 existingObj 属性值的类型,因为它可能是统一的或 nil

子问题 B:如果只有属性的名称,如何测试属性的类型?

我想就是这样。我想这一定是这里已经存在的东西的骗局,但我找不到它。也许你们可以?
干杯,EP。

PS 如果您有更好的方法将自定义 Objective-C 对象同步到反序列化 JSON 对象,请分享!最后,结果才是最重要的。

In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.

For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:

  1. Not all properties of the existingObj are present in the updateDict
  2. Not all properties of the updateDict are available in the extistingObj.
  3. Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
  4. updateDict may contain values for keys that are uninitialized (nil) in existingObj.

This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:

// key is an NSString, e.g. @"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
    [existingObj setValue:[updateDict objectForKey:key] forKey:key];
}

Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.

This makes for subquestion A: How does one test for a property setter, if you only have the property's name?

The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key @"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key @"color" that is @"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:

if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
     // regular assignment
} else {
     // perform custom assignment
}

Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.

Subquestion B: How does one test for the type of a property, if you only have the property's name?

I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.

P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

围归者 2024-10-23 16:45:02

如果你想查询一个对象是否有一个名为key的给定KVC键的setter,它对应于一个声明的属性,你需要检查它是否响应一个名为setKey:<的选择器方法/code> (以 set 开头,将 key 中的第一个字符大写,添加尾随冒号)。例如,有

NSString *key = @"displayName";
NSString *setterStr = [NSString stringWithFormat:@"set%@%@:",
                       [[key substringToIndex:1] capitalizedString],
                      [key substringFromIndex:1]];

if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
    NSLog(@"found the setter!");
    [obj setValue:someValue forKey:key];
}

两点注释:

  • 尽管属性的 setter 名称可能不遵循上述模式,但它们不符合 KVC 标准,因此检查 set:< /code> 因为您使用 KVC 设置相应的值。

  • KVC 不仅仅使用 setter 方法。如果它没有找到 setter 方法,它会检查该类是否允许直接访问实例变量,如果是,则使用实例变量来设置值。此外,如果没有找到 setter 方法或实例变量,它会将 -setValue:forUndefinedKey: 发送到接收者,接收者的类可能会覆盖引发异常的标准实现。 键值编码编程指南。也就是说,如果您总是使用属性,检查 setter 方法应该是安全的。

至于你的第二个问题,不可能查询运行时来了解属性的实际 Objective-C 类。从运行时的角度来看,属性一般类型(例如方法参数/返回类型)。这种类型编码对任何 Objective-C 对象都使用单一编码(即 @),因此 NSString 属性的类型编码与 @ 属性的类型编码相同。 code>UIColor 属性,因为它们都是 Objective-C 类。

如果您确实需要此功能,一种替代方法是处理您的类并添加一个类方法,该方法返回一个字典,其中包含该类和超类中声明的每个属性(或您感兴趣的属性)的键和相应类型,或者也许某种描述语言。您必须自己执行此操作并依赖运行时不可用的信息。

If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,

NSString *key = @"displayName";
NSString *setterStr = [NSString stringWithFormat:@"set%@%@:",
                       [[key substringToIndex:1] capitalizedString],
                      [key substringFromIndex:1]];

if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
    NSLog(@"found the setter!");
    [obj setValue:someValue forKey:key];
}

Two remarks:

  • Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.

  • KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.

As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely @) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.

If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文