首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >检查GKScore实例是否具有上下文属性

检查GKScore实例是否具有上下文属性
EN

Stack Overflow用户
提问于 2012-08-26 07:33:06
回答 4查看 548关注 0票数 1

对于我的游戏的联机模式,我使用的是GKScorecontext属性,并且由于所有支持游戏中心的设备都可以更新到iOS 5(此时添加了context属性),因此我要求context属性可用于联机游戏。然而,我在实现这个运行时检查时遇到了问题。我假设我可以使用[GKScore instancesRespondToSelector:@selector(setContext:)]检查它的存在,但这在iOS 5和5.1模拟器以及@selector(context)上都返回false。究竟为什么会发生这种情况,请问执行这种检查的最干净、最正确的方法是什么?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-08-29 13:37:58

我不能完全解释这一点,但是GKScore类的实例化对象会将YES返回给repondsToSelector(context),即使该类说它不会。如果其他解决方案都不起作用,那么构造一个GKScore对象来查询它。

我想知道[[GKScore alloc] init]是否真的返回一个类型不是GKScore的对象。这是可能发生的。

代码语言:javascript
运行
复制
GKScore *instantiatedScore = [[GKScore alloc] init]; // Add autorelease if using manual reference counting.
NSString* className = NSStringFromClass([instantiatedScore class]);
NSLog(@"instantiatedScore class name = %@", className);

但是,根据下面的输出,它不是这样的:

代码语言:javascript
运行
复制
instantiatedScore class name = GKScore

我想知道GKSCore.h头文件中的编译器指令是否会影响这一点。它定义了两个仅在iOS 5.0或更高版本中可用的属性:contextshouldSetDefaultLeaderboard。也许这些编译器指令意味着类不能保证它将支持这两个属性。

在这个假设下,[GKScore instancesRepondToSelector:@selector(category)]应该返回YES,但是[GKScore instancesRepondToSelector:@selector(shouldSetDefaultLeaderboard)]应该返回NO

代码语言:javascript
运行
复制
GKScore *instantiatedScore = [[GKScore alloc] init]; // Add autorelease if using manual reference counting.
NSLog(@"GKScore category = %d", [GKScore instancesRespondToSelector:@selector(category)]);
NSLog(@"instantiatedScore category = %d", [instantiatedScore respondsToSelector:@selector(category)]);

NSLog(@"GKScore context = %d", [GKScore instancesRespondToSelector:@selector(context)]);
NSLog(@"instantiatedScore context = %d", [instantiatedScore respondsToSelector:@selector(context)]);

NSLog(@"GKScore shouldSetDefaultLeaderboard = %d", [GKScore instancesRespondToSelector:@selector(shouldSetDefaultLeaderboard)]);
NSLog(@"instantiatedScore shouldSetDefaultLeaderboard = %d", [instantiatedScore respondsToSelector:@selector(shouldSetDefaultLeaderboard)]);

但是,输出结果比这更奇怪:

代码语言:javascript
运行
复制
GKScore category = 0
instantiatedScore category = 1
GKScore context = 0
instantiatedScore context = 1
GKScore shouldSetDefaultLeaderboard = 1
instantiatedScore shouldSetDefaultLeaderboard = 1
票数 1
EN

Stack Overflow用户

发布于 2012-09-02 20:49:22

这看起来像是GK实现中的一个bug。

考虑下面的代码...

代码语言:javascript
运行
复制
// Get the C-functions that are really called when the selector message is sent...
typedef BOOL (*XX)(id, SEL, SEL);
XX classImpNSObject = (XX)[NSObject
    methodForSelector:@selector(instancesRespondToSelector:)];
XX classImpGKScore = (XX)[GKScore
    methodForSelector:@selector(instancesRespondToSelector:)];
XX instImpNSObject = (XX)[NSObject
    instanceMethodForSelector:@selector(respondsToSelector:)];
XX instImpGKScore = (XX)[GKScore
    instanceMethodForSelector:@selector(respondsToSelector:)];

// See that the same C function is called for both of these...
NSLog(@"instancesRespondToSelector: %p, %p", classImpNSObject, classImpGKScore);

// But, different functions are called for these...
NSLog(@"respondsToSelector: %p, %p", instImpNSObject, instImpGKScore);

// Invoke to C-Functions for instancesRespondToSelector:
NSLog(@"NSObject instancesRespondToSelector: context: %s",
    classImpNSObject(
        [NSObject class],
        @selector(instancesRespondToSelector:),
        @selector(context))
    ? "YES" : "NO");
NSLog(@"GKScore instancesRespondToSelector: context: %s",
    classImpGKScore(
        [GKScore class],
        @selector(instancesRespondToSelector:),
        @selector(context))
    ? "YES" : "NO");

// Invoke the C functions for respondsToSelector:
GKScore *gkScore = [[GKScore alloc] init];
NSLog(@"NSObject respondsToSelector: context: %s",
    instImpNSObject(
        gkScore,
        @selector(respondsToSelector:),
        @selector(context))
    ? "YES" : "NO");
NSLog(@"GKScore respondsToSelector: context: %s",
    instImpGKScore(
        gkScore,
        @selector(respondsToSelector:),
        @selector(context))
    ? "YES" : "NO");

基本上,我们只是提取了在响应这些消息时调用的C函数。

如您所见,NSObject和GKScore对instancesRespondToSelector:使用完全相同的C函数实现。但是,它们对respondsToSelector:使用不同的C函数实现。这意味着GKScore用自己的实现覆盖了respondsToSelector: (但不覆盖instancesRespondToSelector

如果您将相同的GKScore实例发送到respondsToSelector:的不同C实现,那么对于某些选择器,您会得到不同的结果(显然,否则就没有理由提供一个子类实现)。

看起来他们为一些特殊的属性做了一些时髦的事情,并为respondsToSelector:提供了一个重写来处理特殊情况,但忘记了确保instancesRespondToSelector:做了正确的事情。

如果你想浏览汇编代码,设置一个断点,我相信你能看出来不同之处。

我没有那么做。

我个人的好奇心只能让我走到这一步:-)

对于试图在代码中检测方法实现的情况,我建议创建一个临时GKScore对象来执行测试,缓存结果,并释放临时对象。

票数 4
EN

Stack Overflow用户

发布于 2012-08-31 08:24:05

如果您专门寻找某个属性的存在,则应该使用Objective-C运行时函数:

代码语言:javascript
运行
复制
class_getProperty(Class cls, const char *name)

要使用它,您必须导入:

代码语言:javascript
运行
复制
#import <objc/runtime.h>

作为一个很小的测试示例,下面是如何测试特定属性的存在:

代码语言:javascript
运行
复制
#import <objc/runtime.h>

//...

objc_property_t realP = class_getProperty([GKScore class], "context");
objc_property_t fakeP = class_getProperty([GKScore class], "fakeContext");


if (realP) {
    NSLog(@"context exists");
}
if (!fakeP) {
    NSLog(@"fakeContext does not exist");
}
// Both statements will log correctly.

至于为什么GKScore实例似乎没有响应正确的选择器,我的想法是context属性可能被声明为@dynamic,因此+instancesRespondToSelector:-respondsToSelector:将返回NO (参见this question)。不知道内部细节,这是我能建议的全部,但是如果你只是想测试一个属性的存在,上面的示例代码可以工作。

顺便说一句,如果你不想让Objective-C运行时的include到处浮动,你可能想把这种行为封装在一个类中,或者把它包装在选择器中,而不是一字不差地把它放在某个地方。当然,这完全取决于您。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12126501

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档