Singleton method not getting called in Cocos2d - objective-c

I am calling a Singleton method that does not get called when I try doing this.
I get no errors or anything, just that I am unable to see the CCLOG message.
Under what reasons would a compiler not give you error and not allow you to call a method?
[[GameManager sharedGameManager] openSiteWithLinkType:kLinkTypeDeveloperSite];
The method is defined as follows:
-(void)openSiteWithLinkType:(LinkTypes)linkTypeToOpen
{
CCLOG(#"WE ARE IN openSiteWithLinkType"); //I NEVER SEE THIS MESSAGE
NSURL *urlToOpen = nil;
if (linkTypeToOpen == kLinkTypeDeveloperSite)
{
urlToOpen = [NSURL URLWithString:#"http://somesite.com"];
}
if (![[UIApplication sharedApplication]openURL:urlToOpen])
{
CCLOG(#"%#%#",#"Failed to open url:",[urlToOpen description]);
[self runSceneWithID:kMainMenuScene];
}
}
HERE IS THE CODE TO MY SINGLETON:
#import "GameManager.h"
#import "MainMenuScene.h"
#implementation GameManager
static GameManager* _sharedGameManager = nil;
#synthesize isMusicON;
#synthesize isSoundEffectsON;
#synthesize hasPlayerDied;
+(GameManager*) sharedGameManager
{
#synchronized([GameManager class])
{
if (!_sharedGameManager)
{
[[self alloc] init];
return _sharedGameManager;
}
return nil;
}
}
+(id)alloc
{
#synchronized ([GameManager class])
{
NSAssert(_sharedGameManager == nil,#"Attempted to allocate a second instance of the Game Manager singleton");
_sharedGameManager = [super alloc];
return _sharedGameManager;
}
return nil;
}
-(id)init
{
self = [super init];
if (self != nil)
{
//Game Manager initialized
CCLOG(#"Game Manager Singleton, init");
isMusicON = YES;
isSoundEffectsON = YES;
hasPlayerDied = NO;
currentScene = kNoSceneUninitialized;
}
return self;
}
-(void) runSceneWithID:(SceneTypes)sceneID
{
SceneTypes oldScene = currentScene;
currentScene = sceneID;
id sceneToRun = nil;
switch (sceneID)
{
case kMainMenuScene:
sceneToRun = [MainMenuScene node];
break;
default:
CCLOG(#"Unknown Scene ID, cannot switch scenes");
return;
break;
}
if (sceneToRun == nil)
{
//Revert back, since no new scene was found
currentScene = oldScene;
return;
}
//Menu Scenes have a value of < 100
if (sceneID < 100)
{
if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad)
{
CGSize screenSize = [CCDirector sharedDirector].winSizeInPixels;
if (screenSize.width == 960.0f)
{
//iPhone 4 retina
[sceneToRun setScaleX:0.9375f];
[sceneToRun setScaleY:0.8333f];
CCLOG(#"GM: Scaling for iPhone 4 (retina)");
}
else
{
[sceneToRun setScaleX:0.4688f];
[sceneToRun setScaleY:0.4166f];
CCLOG(#"GM: Scaling for iPhone 3G or older (non-retina)");
}
}
}
if ([[CCDirector sharedDirector] runningScene] == nil)
{
[[CCDirector sharedDirector] runWithScene:sceneToRun];
}
else
{
[[CCDirector sharedDirector] replaceScene:sceneToRun];
}
}
-(void)openSiteWithLinkType:(LinkTypes)linkTypeToOpen
{
CCLOG(#"WE ARE IN openSiteWithLinkType");
NSURL *urlToOpen = nil;
if (linkTypeToOpen == kLinkTypeDeveloperSite)
{
urlToOpen = [NSURL URLWithString:#"http://somesite.com"];
}
if (![[UIApplication sharedApplication]openURL:urlToOpen])
{
CCLOG(#"%#%#",#"Failed to open url:",[urlToOpen description]);
[self runSceneWithID:kMainMenuScene];
}
}
-(void) test
{
CCLOG(#"this is test");
}
#end

Perhaps sharedGameManager is returning nil? This won't cause an error.
Edit: The issue is in the new code you posted:
if (!_sharedGameManager) {
[[self alloc] init];
return _sharedGameManager;
}
return nil;
If _sharedGameManager is non-nil, this will return nil. You want:
if (!_sharedGameManager) {
[[self alloc] init];
}
return _sharedGameManager;

Check this Cocoa With Love article on how to make a proper singleton.

Related

Getting memory warnings after starting a new Cocos2d-iPhone project in ARC. Beginner

Have recently been teaching myself objective-c in order to learn Cocos2d.
Started a new project to teach myself the basics with a classic falling gems tetris-like game. The first step I took was to enable the project for ARC using edit > refactor, and selecting the last 4 files in the template.
So far have been able to add a single gem with a dynamically colored sprite through subclassing CCSprite, and move it along the XCoordinates while having it fall from the top. The gems are given a gemPos location – eg 5,3 – in order for me to later implement the matching methods.
Am getting an EXC_BAD_ACCESS warning when a few of the blocks stack up on top of each other after testing out the project for a while. Am confused as to why this is happening if ARC is enabled.
My clumsy code is as follows (have left out .h files):
Default helloworld layer:
-(id) init
{
if( (self=[super init]) ) {
oldGems = [[NSMutableArray alloc]init];
[self setIsTouchEnabled:YES];
[self newGem];
[self scheduleUpdate];
}
return self;
}
- (void)registerWithTouchDispatcher
{
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
oldPos = touchLocation;
return TRUE;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
[self updateXCoord:(CGPoint)touchLocation];
}
- (void)updateXCoord:(CGPoint)touchLocation
{
CGPoint distance = ccpSub(touchLocation, oldPos);
float xDistance = distance.x;
if (abs(xDistance) >= 36) {
if (touchLocation.x > oldPos.x) {
[_thisGem setPosition:ccp([_thisGem position].x + 36, [_thisGem position].y)];
[_thisGem setGemPos:ccp([_thisGem gemPos].x+1,[_thisGem gemPos].y)];
NSLog(#"The gem position is %#", NSStringFromCGPoint([_thisGem gemPos]));
oldPos = touchLocation;
} else {
[_thisGem setPosition:ccp([_thisGem position].x - 36, [_thisGem position].y)];
[_thisGem setGemPos:ccp([_thisGem gemPos].x-1,[_thisGem gemPos].y)];
NSLog(#"The gem position is %#", NSStringFromCGPoint([_thisGem gemPos]));
oldPos = touchLocation;
}
}
}
- (void)newGem
{
if (_thisGem) {
PCGem *oldGem = _thisGem;
NSLog(#"Old gem position at %#", NSStringFromCGPoint([oldGem gemPos]));
[oldGems addObject:oldGem];
}
_thisGem = [[PCGem alloc] initWithGemColor];
[_thisGem setPosition:ccp(160, 450)];
[self addChild:_thisGem];
[_thisGem setGemPos:ccp(4,10)];
NSLog(#"Gem added with %# color", [_thisGem gemColorName]);
}
- (void)update:(ccTime)dt
{
if ([_thisGem gemPos].y > 0) {
[self spaceBelowOccupied];
[_thisGem setPosition:ccp([_thisGem position].x, [_thisGem position].y-1)];
[_thisGem setGemPos:ccp([_thisGem gemPos].x, floor([_thisGem position].y / 36))];
} else {
[self newGem];
}
}
- (void)spaceBelowOccupied
{
for (PCGem *occupiedTile in oldGems) {
if (CGRectIntersectsRect(occupiedTile.boundingBox, _thisGem.boundingBox)) {
NSLog(#"Collision detected!");
[self newGem];
}
}
}
And my clumsy PCGem class:
- (id)initWithGemColor
{
if ((self = [super initWithFile:#"gemGS.png"])) {
NSArray *gemColorList = [NSArray arrayWithObjects:(NSString *)#"blue", (NSString *)#"red", (NSString *)#"green", nil];
NSInteger randColor = arc4random_uniform(3);
switch (randColor) {
case 0:
{
[self setGemColorName:[gemColorList objectAtIndex:0]];
ccColor3B gemColorRGB = {0,0,255};
[self setColor:gemColorRGB];
break;
}
case 1:
{
[self setGemColorName:[gemColorList objectAtIndex:1]];
ccColor3B gemColorRGB = {255,0,0};
[self setColor:gemColorRGB];
break;
}
case 2:
{
[self setGemColorName:[gemColorList objectAtIndex:2]];
ccColor3B gemColorRGB = {0,255,0};
[self setColor:gemColorRGB];
break;
}
}
}
return self;
}
Which leads me to another question, which I can't seem to find the answer to. In examples of ARC enabled Cocos2d projects, I've seen the node convenience method used, which of course is alloc]init]autorelease]. But I thought you can't use Autorelease with ARC and this would cause a crash? How does this work?
Any ideas to my woes? Would greatly appreciate clarification :)
Cheers,
Adrian
I think it might be caused by editing an array while enumerating through it. In your update function you call spaceBelowOccupied:
- (void)spaceBelowOccupied
{
for (PCGem *occupiedTile in oldGems) {
if (CGRectIntersectsRect(occupiedTile.boundingBox, _thisGem.boundingBox)) {
NSLog(#"Collision detected!");
[self newGem];
}
}
}
then if newGem gets called while in your for loop and this loop succeeds:
if (_thisGem) {
PCGem *oldGem = _thisGem;
NSLog(#"Old gem position at %#", NSStringFromCGPoint([oldGem gemPos]));
[oldGems addObject:oldGem];
}
then you are adding an object to an array while enumerating through it. So change your spaceBelowOccupied to this and see if it works:
- (void)spaceBelowOccupied
{
for (PCGem *occupiedTile in [oldGems copy]) {
if (CGRectIntersectsRect(occupiedTile.boundingBox, _thisGem.boundingBox)) {
NSLog(#"Collision detected!");
[self newGem];
}
}
}
that way you make a copy of oldGems to enumerate through that will get autoreleased once you are done going through it.

Block does not react to outside property change

After some questions here I decided to run a server like this:
#implementation Server
-(id)init
{
if (self = [super init])
{
shouldRun = true;
__block Server* blocksafeSelf = self; // should prevent retain cycle
myServer = ^() {
dispatch_async(dispatch_get_main_queue(), ^{
NSString* currentText = controller.output.text;
controller.outputTextField.text = [currentText stringByAppendingString:#"Server ready. \n"];
});
while (blocksafeSelf.shouldRun) {
int bytes_received = recvfrom(...);
if (bytes_received > 0) {
switch (TYPE OF RECEIVED PACKET) {
case 1: {
dispatch_async(dispatch_get_main_queue(), ^{
NSString* currentText = controller.outputTextField.text;
controller.outputTextField.text = [currentText stringByAppendingString:#"S: Received Typ 1 "];
});
[blocksafeSelf methodToDealWithPacket:(PACKETTYPE*) buffer];
break;
}
...
default: {
...
}
}
}
}
return self;
}
#synthesize shouldRun;
the corresponding .h file:
#interface Server : NSObject {
ServerBlock myServer;
....
}
#property BOOL shouldRun;
....
#end
Now I have a ViewController, which has the Server as a property and following method in its implementation:
// called when the user clicks the stop button
- (IBAction) clickedStop
{
if(theServer != nil) {
theServer.shouldRun = false;
}
}
When I click the stop button however, the server does not exit his while loop. Why?
edit: Here is, how the server is started in the ViewController:
// called when the user clicks the start button
- (IBAction) clickedStart
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
if (...) { // all needed data has been entered
// start server here
if(theServer == nil) {
theServer = [[SNPTServer alloc] initWithHost:serverIP.text AndPort: [serverPort.text intValue] AndViewController:self];
dispatch_async(queue, theServer.myServer);
}
}
}
As you see, I have altered the constructor above a bit.
My answer will only work if the body of your while loop looks similar to the following (I will assume it does)
while (blocksafeSelf.shouldRun) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
In this case, just setting shouldRun to NO is not enough. That while loop is running on a different thread/runLoop so an event must happen to cause the run loop to pop so that the condition is checked again.
The easiest way to do that is to set shouldRun to NO from the thread that server is running on. I would do something like this:
myServer = ^() {
self.runThread = [NSThread currentThread]; // runThread declared elsewhere
while (blocksafeSelf.shouldRun) {
....
}
}
And then when you want to cancel it do:
- (IBAction) clickedStop
{
if ([NSThread currentThread] == theServer.runThread) {
if(theServer != nil) {
theServer.shouldRun = NO;
}
} else if (theServer.runThread) {
[self performSelector:_cmd
onThread:theServer.runThread
withObject:nil
waitUntilDone:NO];
}
}

Crash while releasing in implementation of NSCoding Protocol methods

Below is the code I use for unarchiving the data:
- (id)initWithCoder:(NSCoder *)decoder
{
if((self = [super init]))
{
NSString *resoureItemClassName = NSStringFromClass([ResourceItem class]);
self.selectedItemClassName=[decoder decodeObjectForKey:#"selectedItemClassName"];
Class class = NSClassFromString(selectedItemClassName);
id temp = nil;
if(class)
{
temp = [[class alloc] init];
}
if(temp)
{
if([temp isKindOfClass:[NSString class]])
{
self.selectedString = [decoder decodeObjectForKey:#"selectedString"];;
self.ID = selectedString;
}
else if([selectedItemClassName isEqualToString:resoureItemClassName])
{
DLog(#"Resource item")
}
[temp release];
}
else
{
self.ID = nil;
}
}
return self;
}
When I release the tempObject, the app crashes. If I do not do it, it is a leak. I tried using the autorelease method. Still then it crashed? What is the solution?
UPDATE:
The encoded data in selectedItemClassName is either __NSCFConstantString or __NSCFString. The crash occurs only with __NSCFString and not with __NSCFConstantString as the class name,
What could be the reason?
How about:
- (id)initWithCoder:(NSCoder *)decoder
{
if((self = [super init]))
{
NSString *resourceItemClassName = NSStringFromClass([ResourceItem class]);
self.selectedItemClassName=[decoder decodeObjectForKey:#"selectedItemClassName"];
Class class = NSClassFromString(selectedItemClassName);
if(class)
{
NSString *stringClassName = NSStringFromClass([NSString class]);
if([stringClassName isEqualToString:self.selectedItemClassName])
{
self.selectedString = [decoder decodeObjectForKey:#"selectedString"];;
self.ID = selectedString;
}
else if([self.selectedItemClassName isEqualToString:resourceItemClassName])
{
DLog(#"Resource item")
}
}
else
{
self.ID = nil;
}
}
return self;
}

tapAtPoint on UIWebView subclass

I have subclassed UIWebView so that I can get touch events and also implemented this handy method. I'm curious, if this will work on an actual iOS device. I'm not at the office, so I don't know if does. It seems to work in the simulator.
- (void) tapAtPoint:(CGPoint)point
{
id /*UIWebBrowserView*/ webBrowserView = nil;
id webViewInternal = nil;
object_getInstanceVariable(self, "_internal", (void **)&webViewInternal);
object_getInstanceVariable(webViewInternal, "browserView", (void **)&webBrowserView);
if (webBrowserView) {
[webBrowserView tapInteractionWithLocation:point];
}
}
Has anyone tried something like this? I for sure find out in the morning, lol.
Please try this code, Here its working fine.
/* TapDetectingWindow.m */
#import "TapDetectingWindow.h"
#implementation TapDetectingWindow
#synthesize viewToObserve;
#synthesize controllerThatObserves;
- (id)initWithViewToObserver:(UIView *)view andDelegate:(id)delegate {
if(self == [super init]) {
self.viewToObserve = view;
self.controllerThatObserves = delegate;
}
return self;
}
- (void)dealloc {
[viewToObserve release];
[super dealloc];
}
- (void)forwardTap:(id)touch {
[controllerThatObserves userDidTapWebView:touch];
}
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
if (viewToObserve == nil || controllerThatObserves == nil)
return;
NSSet *touches = [event allTouches];
if (touches.count != 1)
return;
UITouch *touch = touches.anyObject;
if (touch.phase != UITouchPhaseEnded)
return;
if ([touch.view isDescendantOfView:viewToObserve] == NO)
return;
CGPoint tapPoint = [touch locationInView:viewToObserve];
NSLog(#"TapPoint = %f, %f", tapPoint.x, tapPoint.y);
NSArray *pointArray = [NSArray arrayWithObjects:[NSString stringWithFormat:#"%f", tapPoint.x],
[NSString stringWithFormat:#"%f", tapPoint.y], nil];
if (touch.tapCount == 1) {
[self performSelector:#selector(forwardTapwithObject:pointArray afterDelay:0.5];
}
else if (touch.tapCount > 1) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(forwardTap  object:pointArray];
}
}
#end
/* WebViewController.h */
#interface WebViewController : UIViewController<TapDetectingWindowDelegate> {
IBOutlet UIWebView *mHtmlViewer;
TapDetectingWindow *mWindow;
}
/* WebViewController.m */
- (void)viewDidLoad {
[super viewDidLoad];
mWindow = (TapDetectingWindow *)[[UIApplication sharedApplication].windows objectAtIndex:0];
mWindow.viewToObserve = mHtmlViewer;
mWindow.controllerThatObserves = self;
}
- (void)userDidTapWebView:(id)tapPoint
{
NSLog(#"TapPoint = %f, %f", tapPoint.x, tapPoint.y);
}
Thanks, Let me know if you face any problems.
short answer: Yes, I tried something like this in the same way and it works on the real devices as well (tested with iOS 6).
ARC version of your method:
- (void) tapAtPoint:(CGPoint)point
{
Ivar internalWebViewIvar = class_getInstanceVariable([self class], "_internal");
id internalWebView = object_getIvar(self, internalWebViewIvar);
Ivar browserViewIvar = class_getInstanceVariable(object_getClass(internalWebView), "browserView");
id browserView = object_getIvar(internalWebView, browserViewIvar);
if (browserView) {
[browserView performSelector:#selector(tapInteractionWithLocation:) withObject:[NSValue valueWithCGPoint:point]];
}
}

TTModel load method not being called

Issue
Description
The following code results in load method of TTModel not being called. I've stepped it through the debugger, as well as stepping through the TTCatalog application. The only difference between the two that I can see, is that when the catalog sets it's DataSource in the createModel method of the controller, TTModel's load method gets called, whereas mine does not.
About the Code
I've commented the specific areas of code, to tell what they should be doing and what problem is happening, but I included all of it for completion's sake.
You should look at specifically
PositionsController's createModel method
PositionsList's load method
Those are the areas where the issue is, and would be the best place to start.
Code
PositionsController : TTTableViewController
- (id)initWIthNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
self.title = #"Positions";
self.variableHeightRows = NO;
self.navigationBarTintColor = [UIColor colorWithHexString:#"1F455E"];
}
return self;
}
//This method here should result in a call to the PositionsList load method
- (void)createModel
{
PositionsDataSource *ds = [[PositionsDataSource alloc] init];
self.dataSource = ds;
[ds release];
}
- (void)loadView
{
[super loadView];
self.view = [[[UIView alloc] initWithFrame:TTApplicationFrame()] autorelease];
self.tableView = [[[UITableView alloc] initWithFrame:TTApplicationFrame() style:UITableViewStylePlain] autorelease];
self.tableView.backgroundColor = [UIColor colorWithHexString:#"E2E7ED"];
self.tableView.separatorColor = [UIColor whiteColor];
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
//self.tableView.delegate = self;
[self.view addSubview:self.tableView];
}
//Override UITableViewDelegate creation method, so we can add drag to refresh
- (id<TTTableViewDelegate>) createDelegate {
TTTableViewDragRefreshDelegate *delegate = [[TTTableViewDragRefreshDelegate alloc] initWithController:self];
return [delegate autorelease];
}
PositionsDataSource : TTListDataSource
#implementation PositionsDataSource
#synthesize positionsList = _positionsList;
-(id)init
{
if (self = [super init])
{
_positionsList = [[PositionsList alloc] init];
self.model = _positionsList;
}
return self;
}
-(void)dealloc
{
TT_RELEASE_SAFELY(_positionsList);
[super dealloc];
}
-(void)tableViewDidLoadModel:(UITableView*)tableView
{
self.items = [NSMutableArray array];
}
-(id<TTModel>)model
{
return _positionsList;
}
#end
PositionsList : NSObject <TTModel>
#implementation PositionsList
//============================================================
//NSObject
- (id)init
{
if (self = [super init])
{
_delegates = nil;
loaded = NO;
client = [[Client alloc] init];
}
return self;
}
- (void)dealloc
{
TT_RELEASE_SAFELY(_delegates);
[client dealloc];
[super dealloc];
}
//==============================================================
//TTModel
- (NSMutableArray*)delegates
{
if (!_delegates)
{
_delegates = TTCreateNonRetainingArray();
}
return _delegates;
}
-(BOOL)isLoadingMore
{
return NO;
}
-(BOOL)isOutdated
{
return NO;
}
-(BOOL)isLoaded
{
return loaded;
}
-(BOOL)isEmpty
{
//return !_positions.count;
return NO;
}
-(BOOL)isLoading
{
return YES;
}
-(void)cancel
{
}
//This method is never called, why is that?
-(void)load:(TTURLRequestCachePolicy)cachePolicy more:(BOOL)more
{
//This method is not getting called
//When the PositionsController calls self.datasource, load should be called,
//however it isn't.
[_delegates perform:#selector(modelDidStartLoad:) withObject:self];
[client writeToServer:self dataToSend:#""];
}
-(void)invalidate:(BOOL)erase
{
}
#end
Short answer: return NO instead of YES for isLoading in your PositionList.
For a longer explanation:
If you dig through the Three20 source, you'll find that setting the dataSource on a view controller sets the model, refreshing the model and potentially calling load. Here is the code called when the TTModelViewController refreshes:
- (void)refresh {
_flags.isViewInvalid = YES;
_flags.isModelDidRefreshInvalid = YES;
BOOL loading = self.model.isLoading;
BOOL loaded = self.model.isLoaded;
if (!loading && !loaded && [self shouldLoad]) {
[self.model load:TTURLRequestCachePolicyDefault more:NO];
} else if (!loading && loaded && [self shouldReload]) {
[self.model load:TTURLRequestCachePolicyNetwork more:NO];
} else if (!loading && [self shouldLoadMore]) {
[self.model load:TTURLRequestCachePolicyDefault more:YES];
} else {
_flags.isModelDidLoadInvalid = YES;
if (_isViewAppearing) {
[self updateView];
}
}
}
Your PositionList object is returning YES for isLoading and NO for isLoaded. This means that Three20 thinks your model is loading when it isn't. You may also need to implement shouldLoad if it doesn't return YES by default.