對於我的遊戲的在線模式,我使用屬性GKScore
,並且所有支持Game Center的設備都可以更新到iOS 5(即添加context
屬性時) ,我要求context
屬性可以在線播放。但是,我在執行此運行時檢查時遇到問題。我假設我可以使用[GKScore instancesRespondToSelector:@selector(setContext:)]
來檢查它的存在,但是這會在iOS 5和5.1模擬器以及@selector(context)
上返回false。爲什麼在這種情況下會發生這種情況,請執行此檢查的最乾淨和最正確的方法是什麼?檢查GKScore實例是否具有上下文屬性
回答
我不能完全解釋這一點,但GKScore
類的一個實例對象返回YES到repondsToSelector(context)
,甚至在班說,它不會。如果沒有其他解決方案可行,請構造一個GKScore
對象來查詢它。
我想知道,如果[[GKScore alloc] init]
實際上返回比GKScore
其他類型的對象。這可能發生。
GKScore *instantiatedScore = [[GKScore alloc] init]; // Add autorelease if using manual reference counting.
NSString* className = NSStringFromClass([instantiatedScore class]);
NSLog(@"instantiatedScore class name = %@", className);
但是,這不,根據該輸出:
instantiatedScore class name = GKScore
我想知道,如果在GKSCore.h
頭文件的編譯器指令可能會影響這一點。它定義了僅在iOS 5.0或更高版本中可用的兩個屬性:context
和shouldSetDefaultLeaderboard
。也許這些編譯器指令意味着類無法保證它將支持這兩個屬性。
在這種假設下[GKScore instancesRepondToSelector:@selector(category)]
應該返回YES,但[GKScore instancesRepondToSelector:@selector(shouldSetDefaultLeaderboard)]
應該返回NO。
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)]);
但是,輸出比怪異:
GKScore category = 0
instantiatedScore category = 1
GKScore context = 0
instantiatedScore context = 1
GKScore shouldSetDefaultLeaderboard = 1
instantiatedScore shouldSetDefaultLeaderboard = 1
另外,爲了記錄,iOS 6按預期行事。而且我幾乎認爲這需要得到NDA的保護:P我不會獎勵賞金,以防某人能夠解釋爲什麼。 – jrtc27
沒問題。我真的很想知道自己這種奇怪行爲的原因。 – Dondragmer
如果我對「iPod2,1」型號名稱/號碼進行了快速而髒的測試以確定iPod Touch 2G(並因此給iOS 4.1+上的所有其他設備發送不同的消息),那麼人們會不會對我皺眉?編輯:沒關係,只是使消息更通用。 – jrtc27
如果你專門找了一個屬性的存在,你應該使用Objective-C運行時函數:
class_getProperty(Class cls, const char *name)
要使用它,你將不得不進口:
#import <objc/runtime.h>
作爲一個小的測試例子,這裏是你如何測試一個特定屬性的存在:
#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情況下,似乎沒有正確的選擇作出迴應,我想到的是,該上下文屬性可以被聲明爲@dynamic
,因此+instancesRespondToSelector:
和-respondsToSelector:
將返回NO
(參見this question)。不知道內部細節,這是我可以建議的,但如果你只是想測試一個屬性的存在,上面的示例代碼將工作。順便提一下,如果你不想讓包含在Objective-C運行時中的include包含在內,你可能希望將此行爲封裝到一個類中,或者將它包裝在一個選擇器中,而不是僅僅將它們保存在某個地方。當然,這完全取決於你。
'-respondsToSelector:'確實返回了'YES'的預期結果,但正如我在上面對上述答案的評論中提到的那樣,這兩個工作在iOS 6 beta版中都如預期的那樣工作。我曾經考慮過使用Objective-C運行時,但總是看起來不那麼優雅,而且,雖然我知道這不是真的,但它似乎很容易打破。無論如何感謝您的答案,但我認爲我會堅持只檢查分配的實例,因爲它似乎工作。 – jrtc27
@ jrtc27所有的好:)只是想我會讓你知道它。另外,-respondsToSelector:本身就是運行時函數class_respondsToSelector(Class,SEL)的包裝,就像NSObject的大部分基本方法一樣。你可以在這裏看到NSObject的實現:http://www.opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm – Ephemera
啊,這是一個很好的鏈接 - 謝謝你!我想你對'class_respondsToSelector'說得很對,但是源代碼使得我的問題中概括的行爲比我原先想象的更加陌生......噢,我*假設*我可以去深入一些程序集調試,但是我的目前的解決方案的作品,所以我很高興:) – jrtc27
這看起來像是GK實現中的一個錯誤。
考慮下面的代碼...
// 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使用完全相同的C函數實現instancesRespondToSelector:
。但是,他們對respondsToSelector:
使用不同的C函數實現。這意味着,GKScore
覆蓋respondsToSelector:
有自己的執行(但不會覆蓋instancesRespondToSelector
。
如果您發送相同的GKScore
實例的respondsToSelector:
你對於某些選擇(明顯不同的結果,不同的C實現,或者就不會有理由提供一個子類實現)。
看起來他們做了一件時髦的少數特殊的屬性,並提供了respondsToSelector:
的覆蓋來處理特殊情況下,卻忘了關於確保instancesRespondToSelector:
做了正確的事情。
如果你想通過彙編代碼來設置一個斷點,我相信你可以看到不同之處。
我沒有那樣做。
我個人的好奇心,只會把我至今:-)
對於你的情況,試圖探測代碼的方法實現,我建議創建一個臨時的GKScore
對象做你的測試,緩存導致,並釋放臨時對象。
是的,我可以證實這種行爲。此外,在iOS 6中,'instancesRespondToSelector:'實現已被覆蓋,解釋了爲什麼它按預期工作。如果你看到了問題的根源,我會獎賞你的賞金,但正確的答案應該保持原樣,作爲第一個答案,這也給了我想辦法忽略的解決辦法。 – jrtc27
我也遇到過這個問題,但在我的情況下與GKTurnBasedMatchParticipant。我做了一個快速轉儲,將#instancesRespondToSelector:發送給此類的每個屬性。
這裏的結果:
1 playerID false
2 lastTurnDate false
3 status true
4 matchOutcome false
5 matchOutcomeString true
6 isWinner true
7 invitedBy false
8 inviteMessage false
9 internal true
注意有多少屬性的報告說,他們不能作爲選擇發送。但是,請注意另外還有一個「內部」屬性。現在查看查詢此內部對象是否會響應屬性選擇器的結果:
1 playerID true
2 lastTurnDate true
3 status true
4 matchOutcome true
5 matchOutcomeString false
6 isWinner false
7 invitedBy true
8 inviteMessage true
9 internal false
因此,許多缺少的屬性都在此處。我想使用非文件化的「內部」功能來解決明顯的Apple錯誤並不是很安全,但仍然有趣。
編輯:經過一天的搗亂,我發現這裏的問題。這些流氓屬性實際上被設置爲轉發方法以轉發到「內部」對象。作爲ObjectiveC noob,我沒有意識到這是一個完全可以接受的事情。
在我的情況下,我不只是試圖檢測一個對象是否響應選擇器,但我實際上也想調用它。因此,應對轉發的一般解決方案是:
(a)檢查響應使用[實例#respondsToSelector:sel]而不是[[實例類] instanceRespondsToSelector:del]的可用性。
(二)要調用可能,也可能不會,被轉發的方法做到這一點:
NSMethodSignature *signature = [instance methodSignatureForSelector:sel];
if (!signature) {
// It's possible this is a message forwarding selector, so try this before giving up.
NSObject *fwd=[instance forwardingTargetForSelector:sel];
if (fwd && (signature= [fwd methodSignatureForSelector:sel]))
// Redirect to the forwarding target
instance=fwd;
else {
// ERROR case - selector is really not supported
}
}
NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:signature];
// Proceed with invocation setup
我希望這是有用的,以防止其他人,因爲我對這個浪費太多的時間。
- 1. 檢查屬性是否具有屬性
- 2. 檢查屬性是否具有DisplayNameAttribute
- 3. 檢查對象是否具有屬性
- 4. 檢查實體是否具有相同的屬性
- 5. rails檢查現有的實例屬性
- 6. 系統屬性檢查實例是否是後端?
- 7. 檢查類的實例是否使用屬性註釋
- 8. 檢查對象實例的屬性是否爲'空'
- 9. XSL檢查是否有子節點具有屬性
- 10. 檢查CSS類是否有屬性 - jQuery
- 11. 檢查一個屬性是否有效
- 12. 檢查UIView是否有屬性
- 13. 檢查元素是否有屬性
- 14. 檢查數據屬性是否有值
- 15. 是否有任何CSS文件具有以下屬性?
- 16. 檢查對象是否具有特定值的屬性
- 17. 檢查Xml節點是否具有屬性
- 18. 在VBA中,如何檢查OLEObject是否具有LinkedCell屬性?
- 19. 檢查是否一個藍色的消息具有屬性
- 20. 如何檢查PHP對象是否具有PHP4中的屬性。
- 21. 的Javascript檢查對象是否具有一定的屬性值
- 22. Javascript:檢查對象是否具有屬性
- 23. 檢查NSArray是否包含具有特定屬性的對象
- 24. jQuery - 檢查數組是否爲空或具有屬性
- 25. 檢查表的字段是否具有NOT NULL屬性集
- 26. 檢查XML節點是否具有Linq C#的屬性?
- 27. 如何檢查模型是否具有某個列/屬性?
- 28. Jquery:檢查屬性ID是否具有特定模式
- 29. 如何檢查對象是否具有某些方法/屬性?
- 30. jquery檢查圖像是否具有標題屬性
你的意思是說「因爲*不*所有支持Game Center的設備都可以升級到iOS 5嗎?」遊戲中心已添加到iOS 4.1中。我的第二代iPod touch卡在iOS 4.2.1上,所以有Game Center,但永遠不會運行iOS 5.0。 – Dondragmer
哦,對,我知道,我認爲第二代iPod Touch將與iPhone 3G相同,但事實並非如此。感謝那些信息。 – jrtc27