I am using Xcode 4 with Cocos2D on Mac OS X 10.6.8. I am trying to use a sprite sheet for an animation. This is the code I have:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"scrollAnimation.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"scrollAnimation.png"];
[self addChild:spriteSheet];
NSMutableArray *animFrames = [NSMutableArray array];
for(int i = 0; i < 10; i++) {
[animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"scroll%d.png", i]]];
}
self.scrollAnim = [CCAnimation animationWithFrames:animFrames delay:0.05f];
self.scroll = [CCSprite spriteWithSpriteFrameName:#"scroll0.png"];
_scroll.position = ccp(_winSize.width*1.5, _winSize.height*1.5);
[spriteSheet addChild:_scroll];
This works alright in the simulator, but when I get a device build, it crashes. I have narrowed it down to adding the objects to the array. If I comment everything out from before the for loop, then just add the spriteFrameByName part, it works. But when I try and put that into a array, the app crashes on a device.
Any help?
The most likely cause for this kind of behavior is that your app occupies all the memory it has and the OS kills it. I am saying this because it is very typical that in such cases you don't get any hints from the crash log, and is also typical when your app works in the simulator but not in the device.
So, to investigate the issue I would first check if there are memory warning traces (if covos2d has tracing enabled) before the crash. That would be a clear sign.
Actually, there is a second possible outcome when you get a memory warning that also leads to a crash. Your app receives a memory warning (look at cocos2d traces); your texture cache gets emptied, so when you try and use a texture you thought you pre cached, the app crashes. But in this case the error would be more verbose. If you think the problem is related to this, have a look at this post: http://labs.freescapes.org/blog/2011/07/cocos2d-and-memory-warnings/
One reason I've found for things working in the simulator but not working on the device is because the name of the file is typed wrong. I'd check to make sure the name of scrollAnimation.plist isn't really ScrollAnimation.plist or scrollanimation.plist and same thing with scroll0.png that it's not really named Scroll0.png
Related
Currently I didn't use ARC in my app, and I tried to create a NSAlert object as a local variable, at the end of function, I didn't release it.
I expected that the app crash with that, the function code is here.
#define GLOBAL_VARIABLE 0
NSString *msgText = [self.defaultValueTextFiled stringValue];
NSString *informativeText = #"Informative Text";
#if !GLOBAL_VARIABLE
NSAlert *alertView = nil;
#endif
if (alertView == nil)
{
alertView = [[NSAlert alloc] init];
[alertView setMessageText:msgText];
[alertView setInformativeText:informativeText];
NSTextField *accessory = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,200,22)];
[accessory setStringValue:#"accessory result"];
[alertView setAccessoryView:accessory];
}
NSView *accessory= nil;
NSInteger result = [alertView runModal];
if (result == NSAlertAlternateReturn)
{
accessory = [alertView accessoryView];
if (accessory != nil)
{
NSTextField *txtFiled = (NSTextField *)accessory;
NSLog(#"%ld", [accessory retainCount]);
NSString *str = [txtFiled stringValue];
NSLog(#"%ld", [accessory retainCount]);
[self.resultValueTextField setStringValue:str];
NSLog(#"%ld", [accessory retainCount]);
}
}
Questions:
(1) There is no [alertView release] in the end, why not crash? It has even no leaks.
(2) Refer to here , the accessory view shouldn't be release. However, I tried to release the view before [alertView runModal], then get its stringValue later, that could works. Why?
(3) The return value of function retainCount is interesting. When I created the alertView object, the retainedCount of alertView is 3. (Why?)
Before [alertView setAccessoryView:accessory], the accessory's retainedCount is 1, then it changed to 2 after executing it. That's normal and right. However, the log result for the above code, they're 20, 21, 22. How did they come from?
Thanks for your attention!
It does leak. Put a breakpoint on dealloc (or create a subclass of NSAlert and add a log statement to dealloc) to show this
The accessry view should be released. You alloc it and then it would be retained by the alertView
Here's a concise guide on when to use retainCount.
The rules for memory management are generally quite simple. Most problems come from overthinking them and trying to guess what other parts of the system are going to do, rather than just following the rules as written.
The first rule of Memory Management:
Use ARC.
If you cannot follow rule one (for instance, you are developing for OS X 10.5 as I do), then here are the rules:
You must balance each call you make to +alloc…, +new…, -…copy… or -retain with a call to -release or -autorelease.
That's really it. Everything else is really just commentary to help you follow that rule. So, you called [NSAlert alloc] and [NSTextField alloc], you need to call release on those objects.
Why doesn't it crash?
Because a leak is not going to crash unless it causes you to run out of memory.
Why doesn't it leak?
The most likely cause is that you're only running it once and then expecting Instruments to detect the leak. It may not show up if you only run it once. It depends on how NSAlert is internally implemented. Instruments finds leaks by walking all the pointers in the system and determining if any accessible pointers still reference a piece of allocated memory. There are many cases when a "leak is not a leak."
But this also suggests you're not running the static analyzer, because the analyzer should definitely have detected that leak. Run the static analyzer (Cmd-Shift-B) all the time and clean up what it finds.
the accessory view shouldn't be release.
That's not what the link you reference says. You shouldn't add an extra release. But in the referenced code, you'll notice that they release the view to balance their own +alloc.
… retainCount …
Never use -retainCount. Not even for debugging. Not even for anything else. There is no point at which retainCount is going to return you a piece of information that is going to be more enlightening than confusing. Why is it greater than you think it should be? Because other objects are retaining the alert view? Which objects? That's not your business. That's an internal implementation detail, subject to change. It could be pending autorelease calls, which show up temporarily as a "too high retainCount". It could be the run loop. It could be an internal controller. It could be anything. Calling retainCount tells you nothing useful.
Now that you understand manual memory management, switch to ARC and think about object graphs rather than retain counts. It is a much better way to deal with memory.
I've got an OS X app syncing a single document through a ubiquity container back and forth to an iOS equivalent app. The iOS app receives data whenever it changes on the Mac side and sends it whenever it changes on the iOS side (so the iOS app is working all around), and the Mac app sends the data whenever it is changed on the Mac side and it receives the data when the app is launched, but it doesn't seem to be checking again for any data while it runs. I'd like it to update with any changes automatically and immediately, like the OS X "Notes" app does from changes on the iOS side.
At launch, this is the relevant function that gets called:
+(NSMutableDictionary *)getAllNotes {
if(allNotes == nil) {
allNotes = [[NSMutableDictionary alloc]initWithDictionary:[[NSUserDefaults standardUserDefaults] dictionaryForKey:kAllNotes]];
cloudDoc = [[CloudDocument alloc]initWithContentsOfURL:[self notesURL] ofType:NSPlainTextDocumentType error:nil];
[cloudDoc saveToURL:[self notesURL] ofType:NSPlainTextDocumentType forSaveOperation:NSSaveOperation error:nil];
}
return allNotes;
}
and that "CloudDocument" class (which is a subclass of NSDocument) includes:
#import "Data.h"
#implementation CloudDocument
-(NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
return [NSKeyedArchiver archivedDataWithRootObject:[Data getAllNotes]];
}
-(BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
NSDictionary *dict = (NSDictionary *)[NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)data];
[Data didReceiveCloudData:dict];
return YES;
}
+(BOOL)autosavesInPlace {
return YES;
}
#end
which kicks it back to:
+(void)didReceiveCloudData:(NSDictionary *)d {
allNotes = [[NSMutableDictionary alloc]initWithDictionary:d];
[[NSUserDefaults standardUserDefaults] setObject:allNotes forKey:kAllNotes];
[cloudDoc updateChangeCount:NSChangeDone];
}
I think the problem is just that I don't have any part of my code that is equivalent to the phrase "check periodically to see if the ubiquity container has changed, and then do..." etc. I'm sure there's a well-known process for this (some notification event in NSDocument or something), but I've searched around and everything I find is either for iOS/UIDocuments instead of OS X/NSDocuments, or it's all theory and over my head without any tangible code samples to comb through and pick apart.
Can anyone help me out with a method for registering that an iCloud document in the ubiquity container has changed, and ideally where to put it (AppDelegate, CloudDocument.m, etc)? I only have one file syncing around, signified by the constant kAllNotes, so I don't need to track a bunch of different files or anything. I'm pretty sure I can use the code that runs at launch to do what needs to be done, I just can't figure out what to do to start the auto-syncing process.
Thank you in advance!
PS I'm still a beginner, so tutorials and code samples are much appreciated.
You're looking for NSMetadataQuery - it does a spotlight-like, continuously running search for any type of file - and is available on both iOS and OS X (indeed on iOS it can only be used for observing changes to the ubiquity container). I don't have any one link on how to use this - the Apple docs are too general to make much sense initially but do a search on 'NSMetadataQuery iCloud' and you should be sorted - there is tons of information out there on this topic.
With NSMetadataQuery you receive a notification every time something in the observed folder system changes, and it's not just applicable to UIDocument files even if a lot of examples are worried about UIDocuments.
Indeed, NSMetadataQuery it is - it feels like a hack but appears to be the only way to monitor changes to standard (non-UIDocument) files.
Here's a sample project I've compiled. It lets you upload an image from the camera roll, then monitors changes to the ubiquitous Documents folder:
https://github.com/versluis/iCloud-Images
When I start my app in the simulator it crashes immediately with "error: memory read failed"
Everything works fine on iPad/iPhone, but the when I add a simple "return;" before the core data lines, the simulator starts up fine:
return;
CCAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
IMPORTANT!!:
It does not run that code at any time, just compiles it. And if I add the "return;" before the same core data connection in another file instead, it runs fine to. Looks like there is some kind of maximum "connection" to core data or something?
I have tried Cleaning Xcode (Normal and clean build folder), cleaning the simulator (Reset content and setting), but with no success.
Additional question: Is there someway I can re-install IOS simulator?
"error: memory read failed". I received same message this morning. And I found it id related to Block.
As we know, if a class has a Block as its member, it ought to be look like this:
#property(nonatomic, copy)BlockType block;
And my mistake was forgot to use copy when add a block to a NSArray instance:
[array addObject:aBlock];
Finally I solved it by this:
MyBlockType copy = [aBlock copy];
[array addObject:copy];
[copy release];
Good luck!
Upate Mar/20/1013
Another situation causes "error: memory read failed".
- (void)blockCalledMethod{
for (BlockType b in _dictionary) {
b(self);
}
}
The key to solve my problem is to iterate NSDictionary by using .allKeys.
But the object in dictionary is Block, so it must be relative with Block.
I have got the same issue with my iPhone simulator. I have clean code in XCode
and i have reset simulator
Hope it will help
In most systems, the default behaviour for "open a new window" is that it appears at the front. This doesn't happen in Cocoa, and I'm trying to find the "correct" way to make this standard behaviour. Most things I've tried only work for a maximum of one window.
I need to open multiple windows on startup:
(N x NSDocuments (one window each)
1 x simple NSWindowController that opens a NIB file.
Things that DON'T work:
Iterate across all the NSDocuments I want to open, and open them.
What happens? ... only the "last" one that call open on comes to the front - the rest are hidden, invisible, nowhere on the screen, until you fast-switch or use the Window menu to find them.
Code:
...documents is an array of NSPersistentDocument's, loaded from CoreData...
[NSDocumentController sharedDocumentController];
[controller openDocumentWithContentsOfURL:[documents objectAtIndex:0] display:YES error:&error];
Manually invoking "makeKeyAndOrderFront" on each window, after it's opened
What happens? nothing different. But the only way I can find to get the NSWindow instance is so horribly hacky it seems totally wrong (but is mentioend in several blogs and mailing list posts)
Code:
[NSDocumentController sharedDocumentController];
NSDocument* openedDocument = [controller openDocumentWithContentsOfURL:[documents objectAtIndex:0] display:YES error:&error];
[[[[openedDocument windowControllers] objectAtIndex:0] window] makeKeyAndOrderFront:nil];
...I know I'm doing this wrong, but I can't find out why/what to do differently :(.
Something that works, usually, but not always:
As above, but just use "showWindow" instead (I took this from the NSDocument guide).
Bizarrely, this sometimes works ... even though it's the exact code that Apple claims they're calling internally. If they're calling it internally, why does it behave different if I re-invoke it after they've already done so?
[[[openedDocument windowControllers] objectAtIndex:0] showWindow:self];
You can just open all the documents without displaying and then tell the documents to show their windows:
NSArray* docs = [NSArray arrayWithObjects:#"doc1.rtf", #"doc2.rtf",#"doc3.rtf",#"doc4.rtf",nil];
for(NSString* doc in docs)
{
NSURL* url = [NSURL fileURLWithPath:[[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:doc]];
NSError* err;
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:url display:NO error:&err];
}
[[[NSDocumentController sharedDocumentController] documents] makeObjectsPerformSelector:#selector(showWindows)];
Won't this work?
For 10.6 or greater
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
This often has something to do with the app itself: your other windows are behind other apps (in particular, behind Xcode!), and would have appeared with a Hide Others command.
The solution to that problem would be that after you send showWindow to all of your windows (making sure you do the key one last), you tell the app to come forward, relative to other apps.
NSApp.activateIgnoringOtherApps(true) // Swift
or
[NSApp activateIgnoringOtherApps:YES]; // Objective-C
See also: How to bring NSWindow to front and to the current Space?
I have a problem of memory allocation (not leak anyway).
My program has a custom Window with a custom View containing a TextField, an ImageView and a Shadow.
Let's say that every 1 second I programmatically update the value of the TextField using [myTextField setStringValue:#"actual string"].
Obviously, every time the TextField get changed, the view is redrawn.
If I look in Activity Monitor I see that every time the TextField is updated, and therefore the view is redrawn, the allocated memory increases. The ImageView isn't supposed to change.
If I comment the line with setStringValue, the program runs without increasing memory at all. (See Update 4.)
Note that Instruments does not report memory leak or unreleased object and View is autorelease'd.
What can cause this?
UPDATE
I post a simplified version of the actual code:
.h
CustomTextField *myTextField;
int level;
#interface Dummy : NSObject {
NSString *level_string;
NSTimer *timer;
}
#end
.m
#implementation Dummy
- (void)awakeFromNib
{
// ...
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(changestring:)
userInfo:nil
repeats:YES];
// ...
}
-(void)changestring:(NSTimer *)timer
{
level++;
level_string = [[NSString alloc] initWithFormat:#"%i",level];
[myTextField setStringValue:level_string];
[level_string release];
}
Where CustomTextField is a NSTextField class.
I don't know if what I'm going to add is important, anyway the custom window, the custom view and the custom textfield are defined and init programmatically in the code and they are not instantiated in interface builder.
UPDATE 2
I was wrong! Even if I comment setStringValue the memory still increases..a lot less, but it still increases..
The strange fact is that Instruments, in any case, reports a size of "Living Object" which remains constant and no leak is reported.
What is happening?
UPDATE 3
I have just used the amazing heapshots feature of Instruments and this is the result.
The memory increase that I see in activity monitor (which is of the order of thousands of kilobytes after few minutes) where does come from?
UPDATE 4
I think I have found what is causing the problem but I'm not able to solve it.
The View has a TextField, an ImageView and a Shadow. To make them appearing correctly on screen without glitches I have added [view setWantsLayer:YES].
If I comment this line, the memory allocation problem is definitely solved.
Now, as long as I need to use that command, how can I do? Am I supposed to release something related with Core Animation? Note that the only one command related with Core Animation is the above one.
Have a look at session 311 - Advanced Memory Analysis with Instruments of WWDC10 session videos. How to download these videos can be found here.
It could be that you see abandoned memory or that there is some caching going on. What is happening if you trigger a low memory warning in the simulator? Goes the memory down?
Also try using the heapshot feature of Instruments: make a heapshot, update the string and show which objects have been created. In the session video mentioned above the procedure is demonstrated.
EDIT: What I forgot: if you cannot figure out what is going on and you can show this behavior of increasing memory allocation without releasing it if a low memory warning is triggered in a simple example application, file a bug at Apple's bugreporter sending in this sample project and an Instruments trace file to show what is going on.
EDIT2: I was wrong assuming that this problem was on Mac OS X. The same principles apply for a Mac OS X application as well. Memory management in Cocoa is almost the same on the iPhone and the Mac. The biggest difference is that on the Mac there is garbage collection available. Of course on the Mac there are no low memory warnings, so leave that out.
What setStringValue: in CustomTextField does? My guess is that you're retaining passed string without releasing old value in your setter.