@@ -46,6 +46,12 @@ @interface CCBFile : CCNode
4646@end
4747
4848
49+ @interface CCBReader ()
50+
51+ @property (nonatomic , copy ) NSString *currentCCBFile;
52+
53+ @end
54+
4955
5056@implementation CCBReader
5157
@@ -308,6 +314,15 @@ - (void) readPropertyForNode:(CCNode*) node parent:(CCNode*)parent isExtraProp:(
308314
309315 [extraPropNames addObject: name];
310316 }
317+
318+ #if DEBUG
319+ if (isExtraProp
320+ && ![self isPropertyKeySettable: name onInstance: node])
321+ {
322+ NSLog (@" *** [PROPERTY] ERROR HINT: Did you set a custom property \" %@ \" ? In file \" %@ \" " , name, _currentCCBFile);
323+ NSLog (@" *** [PROPERTY] ERROR HINT: Make sure the class \" %@ \" is KVC compliant and \" %@ \" can be set" , [node class ], name);
324+ }
325+ #endif
311326
312327 if (type == kCCBPropTypePosition )
313328 {
@@ -676,7 +691,16 @@ - (void) readPropertyForNode:(CCNode*) node parent:(CCNode*)parent isExtraProp:(
676691 NSString * path = [[CCFileUtils sharedFileUtils ] fullPathForFilename: ccbFileName];
677692 NSData * d = [NSData dataWithContentsOfFile: path];
678693
679- NSAssert (d,@" Failed to find ccb file: %@ " ,ccbFileName);
694+ #if DEBUG
695+ // Special case: scroll view missing content node
696+ if (!d && [ccbFileName isEqualToString: @" .ccbi" ] && [NSStringFromClass ([node class ]) isEqualToString: @" CCScrollView" ])
697+ {
698+ NSLog (@" *** [PROPERTY] ERROR HINT: Did you forget to set the content node for your CCScrollView?" );
699+ }
700+ #endif
701+
702+ NSAssert (d,@" [PROPERTY] %@ - kCCBPropTypeCCBFile - Failed to find ccb file: \" %@ \" , node class name: \" %@ \" , name: \" %@ \" , in ccb file: \" %@ \" " ,
703+ name, ccbFileName, [node class ], [node name ], _currentCCBFile);
680704
681705 CCBReader* reader = [[CCBReader alloc ] init ];
682706 reader.animationManager .rootContainerSize = parent.contentSize ;
@@ -706,8 +730,74 @@ - (void) readPropertyForNode:(CCNode*) node parent:(CCNode*)parent isExtraProp:(
706730 }
707731 else
708732 {
709- NSLog (@" CCBReader: Failed to read property type %d " ,type);
733+ NSAssert (false , @" [PROPERTY] %@ - Failed to read property type %d , node class name: \" %@ \" , name: \" %@ \" , in ccb file: \" %@ \" " ,
734+ name, type, [node class ], [node name ], _currentCCBFile);
735+ }
736+ }
737+
738+ - (BOOL )isPropertyKeySettable : (NSString *)key onInstance : (id )instance
739+ {
740+ if (!key || !instance || ([key length ] == 0 ))
741+ {
742+ return NO ;
710743 }
744+
745+ NSString *firstCharacterOfKey = [[key substringWithRange: NSMakeRange (0 , 1 )] uppercaseString ];
746+ NSString *uppercaseKey = [key stringByReplacingCharactersInRange: NSMakeRange (0 , 1 ) withString: firstCharacterOfKey];
747+ NSString *setterName = [NSString stringWithFormat: @" set%@ " , uppercaseKey];
748+
749+ if ([instance respondsToSelector: NSSelectorFromString (setterName)])
750+ {
751+ return YES ;
752+ }
753+
754+ NSArray *setOfDirectlySettableIvarNames = @[[NSString stringWithFormat: @" _%@ " , key],
755+ [NSString stringWithFormat: @" _is%@ " , uppercaseKey],
756+ key,
757+ [NSString stringWithFormat: @" is%@ " , uppercaseKey]];
758+
759+ return [self doesIvarNameExistInClassHierarchy: [instance class ] searchForIvarNames: setOfDirectlySettableIvarNames];
760+ }
761+
762+ - (BOOL )doesIvarNameExistInClassHierarchy : (Class )class searchForIvarNames : (NSArray *)searchedIvarNames
763+ {
764+ if ([class accessInstanceVariablesDirectly ])
765+ {
766+ NSArray *ivarNames = [self getIvarNamesOfClass: class];
767+
768+ for (NSString *ivarName in ivarNames)
769+ {
770+ if ([searchedIvarNames containsObject: ivarName])
771+ {
772+ return YES ;
773+ }
774+ }
775+ }
776+
777+ Class superClass = class_getSuperclass (class);
778+ if (superClass)
779+ {
780+ return [self doesIvarNameExistInClassHierarchy: superClass searchForIvarNames: searchedIvarNames];
781+ }
782+
783+ return NO ;
784+ }
785+
786+ - (NSArray *)getIvarNamesOfClass : (Class )class
787+ {
788+ NSMutableArray *result = [NSMutableArray array ];
789+ unsigned int iVarCount;
790+
791+ Ivar *vars = class_copyIvarList (class, &iVarCount);
792+ for (int i = 0 ; i < iVarCount; i++)
793+ {
794+ Ivar var = vars[i];
795+ NSString *ivarName = [NSString stringWithCString: ivar_getName (var) encoding: NSUTF8StringEncoding];
796+ [result addObject: ivarName];
797+ }
798+ free (vars);
799+
800+ return result;
711801}
712802
713803- (CCBKeyframe*) readKeyframeOfType : (int )type
@@ -798,7 +888,10 @@ - (CCNode*) readNodeGraphParent:(CCNode*)parent
798888 Class class = NSClassFromString (className);
799889 if (!class)
800890 {
801- NSAssert (false ,@" CCBReader: Could not create class of type %@ " ,className);
891+ #if DEBUG
892+ NSLog (@" *** [CLASS] ERROR HINT: Did you set a custom class for a CCNode? Please check if the specified class name is spelled correctly and available in your project." );
893+ #endif
894+ NSAssert (nil , @" [CLASS] Could not create class named: \" %@ \" . in CCB file: \" %@ \" " , className, _currentCCBFile);
802895 return NULL ;
803896 }
804897 CCNode* node = [[class alloc ] init ];
@@ -937,8 +1030,8 @@ - (CCNode*) readNodeGraphParent:(CCNode*)parent
9371030 if (numPoints > 0 )
9381031 body = [CCPhysicsBody bodyWithCircleOfRadius: cornerRadius andCenter: points[0 ]];
9391032 }
940- NSAssert (body, @" Unknown body shape" );
941-
1033+ NSAssert (body, @" [PHYSICS] Unknown body shape %i , class name \" %@ \" , in CCB file: \" %@ \" " , bodyShape, className, _currentCCBFile );
1034+
9421035 BOOL dynamic = readBool (self);
9431036 BOOL affectedByGravity = readBool (self);
9441037 BOOL allowsRotation = readBool (self);
@@ -1190,7 +1283,9 @@ - (CCNode*) nodeGraphFromFile:(NSString*) file owner:(id)o parentSize:(CGSize)pa
11901283
11911284 NSString * path = [[CCFileUtils sharedFileUtils ] fullPathForFilename: file];
11921285 NSData * d = [NSData dataWithContentsOfFile: path];
1193-
1286+
1287+ self.currentCCBFile = file;
1288+
11941289 return [self loadWithData: d owner: (id )o];
11951290}
11961291
0 commit comments