声明的属性是一个子类,但我的视图控制器认为它是它的超类



背景信息

我有一个正在运行cocos2d场景的视图控制器(这样我就可以将UIkit对象放在场景的顶部)。

我的应用程序崩溃,出现以下错误:

2014-10-25 11:20:04.426 AppName[24166:992733] -[CCScene avatar]: unrecognized selector sent to instance 0x7c5a3270
2014-10-25 11:20:04.428 AppName[24166:992733] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CCScene avatar]: unrecognized selector sent to instance 0x7c5a3270'

我知道该应用程序崩溃的原因是它试图在CCScene上调用getter方法avatar,而不是作为CCScene子类的CHCreateAvatarScene。如果我查看调试器,VC会认为我的currentScene属性的类型是CCScene,而不是CHCreateAvatarScene,所以很明显它找不到Avatar属性。我是不是说错了?我不明白为什么会这样。我也是一个编程新手,仅供参考。这可能是一个明显的错误。

CHCreateAvatarViewController.h

#import "CHCreateAvatarViewController.h"
#import "CHCreateAvatar.h"
#import "CHAvatarAttribute.h"
#import "CHAvatarAttributeOption.h"
#import "CHAttributeData.h"
#import "CHCreateAvatarScene.h"
#import "CHAttachment.h"
@interface CHCreateAvatarViewController () <CocosViewControllerDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
...
@property (strong, nonatomic) CHCreateAvatarScene *currentScene;
...
@end
@implementation CHCreateAvatarViewController
...
#pragma mark - CocosViewControllerDelegate
-(CCScene *)cocosViewControllerSceneToRun:(CocosViewController *)cocosViewController
{
//This will load the Spritebuilder file which is a loaded as a CCScene. 
// I then told it to expect a CHCreateAvatarScene because otherwise I was getting an 'invalid pointer' error. 
// I also tried changing the return type of this method to CHCreateAvatarScene to see if that would have any effect but it didn't, so I changed it back.
self.currentScene = (CHCreateAvatarScene *)[CCBReader loadAsScene:@"CreateAvatarScene"];
        [self setupSpritesWithAttachments:self.factory.attachments];
        return self.currentScene;
    }
...
-(void)setupSpritesWithAttachments:(NSMutableArray *)attachments
{
    int i = 0;
    //This is where its crashing
    for (CCSprite  __strong *sprite in self.currentScene.avatar.attachmentSprites) {
        CHAttachment *attachment = attachments[i];
        sprite.texture = attachment.texture;
        i++;
    }
}
...

CHCreateAvatarScene

// .h
#import "CCScene.h"
#import "CHAvatar.h"
@interface CHCreateAvatarScene : CCScene
@property (strong, nonatomic) CHAvatar *avatar;
@end
//.m
#import "CHCreateAvatarScene.h"

@implementation CHCreateAvatarScene {
    CCNode *avatarNode;
}
-(void)didLoadFromCCB
{
    self.avatar = (CHAvatar *)[CCBReader load:@"Avatar"];
    [avatarNode addChild:self.avatar];
}

CHAvatar(我认为它不相关,但包括它只是以防万一)

//.h
#import "CCNode.h"
@interface CHAvatar : CCNode
@property (strong, nonatomic) NSMutableArray *attachmentSprites;
@end
//.m
#import "CHAvatar.h"
@implementation CHAvatar {
    CCSprite *_shoulders;
    CCSprite *_neck;
    CCSprite *_head;
}
//Have left off the head for now just to get this working.
-(void)didLoadFromCCB
{
    self.attachmentSprites = [@[_shoulders, _neck] mutableCopy];
}
@end

提前感谢您的帮助!

变量的声明类型向编译器表达意图。"我打算把这种类型的东西存储在这个存储器中。"编译器会为这种类型的变量留出适当的存储量(在这种情况下,是一个指针),并试图警告代码显然试图把错误类型的东西放入变量的情况。但它只能在编译时进行静态检查。它不会在运行时进行动态检查。它不会检查代码实际在做什么

重要的是,指针变量的声明类型并不能控制存储在其中的任何指针所指向的实际类型。仅仅因为你已经声明了你的意图,并不意味着你的操作(即你的代码)符合这个意图。

在您的情况下,表达式[CCBReader loadAsScene:@"CreateAvatarScene"]实际上返回的是CCScene的实例,而不是CHCreateAvatarScene的实例。您有一个类型转换来告诉编译器将返回值视为指向CHCreateAvatarScene的指针。这使编译器不会抱怨,但实际上并没有改变指针指向的对象的性质

您在一些地方写道,"视图控制器认为"对象属于错误的类,因此找不到属性。这完全是倒退。编写该代码是为了"认为"对象的类型始终是CHCreateAvatarScene,但事实并非如此。视图控制器不必"查找"该属性。它只是通过调用getter方法来表现为该属性存在。接收到该消息的对象不知道如何响应,因为它实际上不是CHCreateAvatarScene。它是一个CCScene对象。

调试器和错误消息对于对象的实际类型都是正确的。

真正的问题是+[CCBReader loadAsScene:]是如何工作的。为什么希望它返回CHCreateAvatarScene的实例?为什么它的行为与您预期的不同,并返回CCScene的实例?

一个朋友帮我弄清楚了,所以我发布了答案。

基本上,我在Cocos2d中混淆了场景和节点的概念。以下是我修复它的方法:

  1. 将CHCreateAvatarViewController的属性更改为类型CCScene *currentScene,并删除cocosViewControllerSceneToRun:方法中的(* CHCreateAvatarScene)强制转换。事实上,在这个解决方案完成后,我可能会一起删除这个属性。

  2. 将CHCreateAvatarScene重命名为CHCreateAvatarNode(我混淆了场景和节点的概念,所以这很有帮助)。将其更改为CCNode的子类,而不是CCScene。

  3. 向vc添加CCNode *avatarNode属性。在vc中的CCBReaderDidLoad:中,添加self.avatarNode = [[CHCreateAvatarNode alloc] init];

  4. 在应用程序最初崩溃的for循环中,将self.currentScene.avatar.attachmentSprites更改为self.avatarNode.avatar.attachmentSprites

瞧!

最新更新