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

时间:2014-10-25 01:18:39

标签: ios objective-c xcode cocos2d-iphone

背景信息

我有一个运行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方法头像,而不是在CCScene的子类CHCreateAvatarScene上调用。如果我查看调试器,VC认为我的currentScene属性是CCScene类型,而不是CHCreateAvatarScene,所以很明显它找不到Avatar属性。我宣布错了吗?我无法弄清楚为什么会这样。我也是一个编程新手,只是FYI。这可能是一个明显的错误。

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

提前感谢您对此的任何帮助!

2 个答案:

答案 0 :(得分:0)

声明的变量类型表达了编译器的意图。 “我打算在这个存储中存储这类东西。”编译器将为该类型的变量(在本例中为指针)预留适当的存储量,并且它将尝试警告代码明显试图将错误类型的事物放入变量的情况。但它只能在编译时进行静态检查。它不会在运行时进行动态检查。它不会检查代码实际正在做什么。

重要的是,声明的指针变量类型不控制存储在其中的任何指针指向的实际事物类型。仅仅因为你宣布了你的意图,这并不意味着你的行为(即你的代码)与那个意图相匹配。

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

你在几个地方写过“视图控制器认为”对象属于错误的类,因此无法找到属性。这完全是落后的。代码编写为“思考”对象始终是CHCreateAvatarScene类型但实际上并非如此。视图控制器不必“查找”该属性。通过调用getter方法,它就像存在属性一样。收到该消息的对象不知道如何响应它,因为它实际上不是CHCreateAvatarScene。这是一个CCScene对象。

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

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

答案 1 :(得分:0)

一位朋友帮我解决了问题,所以我发布了答案。

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

  1. 将CHCreateAvatarViewController的属性更改为CCScene *currentScene,并在(* CHCreateAvatarScene)方法中删除cocosViewControllerSceneToRun:投射。事实上,在完成此解决方案之后,我可能会一起删除此属性。

  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

  5. 瞧!