Why do I have memory problems? - objective-c

I got this error from XCode:
objc[8422]: FREED(id): message release sent to freed object=0x3b120c0
I googled and find that is related to the memory. But I don't know which line of code I go wrong, any ideas? After I launch my app in simulator, it prompts a second, than, no other error except the error above.
#implementation MyAppDelegate
#synthesize window;
#synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
[window addSubview:viewController.view];
[window makeKeyAndVisible];
[self changeScene:[MainGameMenuScene class]];
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
- (void) changeScene: (Class) scene
{
BOOL animateTransition = true;
if(animateTransition){
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:window cache:YES]; //does nothing without this line.
}
if( viewController.view != nil ) {
[viewController.view removeFromSuperview]; //remove view from window's subviews.
[viewController.view release]; //release gamestate
}
viewController.view = [[scene alloc] initWithFrame:CGRectMake(0, 0, IPHONE_WIDTH, IPHONE_HEIGHT) andManager:self];
//now set our view as visible
[window addSubview:viewController.view];
[window makeKeyAndVisible];
if(animateTransition){
[UIView commitAnimations];
}
}

[viewController.view release]; //release gamestate
That's an extra release (if you didn't alloc it, don't release it). A couple lines down:
viewController.view = [[scene alloc] initWithFrame:CGRectMake(0, 0, IPHONE_WIDTH, IPHONE_HEIGHT) andManager:self];
will release viewController.view again before retaining the new value, that will over-release the view and cause a crash (it sends the release message to an object that has already been dealloc'd / memory that has already been freed). Instead, try:
scene *view = [[scene alloc] initWithFrame:CGRectMake(0, 0, IPHONE_WIDTH, IPHONE_HEIGHT) andManager:self];
viewController.view = view; // setView will release the old/retain the new
[view release]; // so you don't need to worry about releasing it later

Got the solution:
- (void) changeScene: (Class) scene
{
BOOL animateTransition = true;
if(animateTransition){
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:window cache:YES]; //does nothing without this line.
}
if( viewController.view != nil ) {
[viewController.view retain]; //retain the view.
[viewController.view removeFromSuperview]; //remove view from window's subviews.
[viewController.view release]; //release gamestate
}
viewController.view = [[scene alloc] initWithFrame:CGRectMake(0, 0, IPHONE_WIDTH, IPHONE_HEIGHT) andManager:self];
//now set our view as visible
[window addSubview:viewController.view];
[window makeKeyAndVisible];
if(animateTransition){
[UIView commitAnimations];
}
}
It needs me to retain the view, but I don't know why this is necessary.

This line:
[viewController.view release];
Is most likely your problem. Never send release to a property of an object except in the class' dealloc method. The view controllers view property accessors will handle retaining and releasing for you.

Related

WARNING: More than 10 instances of ADBannerView or ADInterstitialView currently exist

I have a iPad app with 2 view controllers; 1st one is for the UI, the 2nd for a view which displays help from a UIWebView. When I go back and forth 10 times between view controllers, I get the message in the title above on exactly the 10th time returning to the first VC.
This is my code in VC #1:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
adView = [[ADBannerView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 50, 320, 50)];
_adBanner = [[ADBannerView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, 320, 50)];
_adBanner.delegate = self;
[self.view addSubview:adView];
}
- (void) viewWillDisappear:(BOOL)animated {
[_adBanner removeFromSuperview];
_adBanner.delegate = nil;
_adBanner = nil;
}
- (void)bannerViewDidLoadAd:(ADBannerView *)banner {
if (!_bannerIsVisible) {
// If banner isn't part of view hierarchy, add it
if (_adBanner.superview == nil)
[self.view addSubview:_adBanner];
// }
[UIView beginAnimations:#"animateAdBannerOn" context:NULL];
// Assumes the banner view is just off the bottom of the screen.
banner.frame = CGRectOffset(banner.frame, 0, -banner.frame.size.height);
[UIView commitAnimations];
_bannerIsVisible = YES;
}
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
NSLog(#"Failed to retrieve ad");
if (_bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOff" context:NULL];
// Assumes the banner view is placed at the bottom of the screen.
banner.frame = CGRectOffset(banner.frame, 0, banner.frame.size.height);
[UIView commitAnimations];
_bannerIsVisible = NO;
}
}
I know why, just don't know how to fix it. Can someone please help me with this?
You create an instance of ADBannerView in viewDidAppear and assign it to adView You don't appear do anything with this and more relevantly don't discard it in viewWillDisappear so each time you pop back to this view controller the previous instance is orphaned when you cycle through viewDidAppear.
Also possibly another issue is that you are not calling [super viewWillDisappear:] in your viewWillDisappear method.

xcode view is called and initialized but not shown

I am having a problem to display a View. When a button is pressed in current view, following action is exected:
- (IBAction) vistaUser: (id)sender{
loginTabController *theInstance = [[loginTabController alloc] init];
[theInstance logIn:user.text :password.text];
}
then, following logIn function is called who have to show userViewControler view but it is not showed. View remains at current one. However, userViewController view is initialized and getData function from this view executed! I do not understand why called view is not displayed! Thanks for help!
- (void)logIn: (NSString *)strUser: (NSString *)strPass
{
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve: UIViewAnimationCurveEaseInOut];
if (self.controladorUser == nil)
{
userViewController *aController = [[userViewController alloc] initWithNibName:#"userViewController" bundle:nil];
self.controladorUser = aController;
[aController release];
}
[UIView setAnimationTransition: UIViewAnimationOptionCurveEaseIn forView:self.view cache:YES];
[self.controladorPass viewWillDisappear:YES];
[self.controladorUser viewWillAppear:YES];
[self.controladorPass.view removeFromSuperview];
[self.view insertSubview:controladorUser.view atIndex:0];
[self.controladorPass viewDidDisappear:YES];
[self.controladorUser viewDidAppear:YES];
[UIView commitAnimations];
//getData call
userViewController *theInstance = [[userViewController alloc] init];
[theInstance getData:strUser :strPass];
}
You are never showing your loginTabController's view. Therefore when you add the UserViewController's view to the tabViewController's view it doesn't do anything. Try this:
- (IBAction) vistaUser: (id)sender{
loginTabController *theInstance = [[loginTabController alloc] init];
[self.view addSubview:theInstance.view];
[theInstance logIn:user.text :password.text];
}
That should get it working, but there are some general code design issues that you may want to address:
Use the newer block animation functions in UIView instead of the older and more difficult "beginAnimations" "commitAnimations" methods.
It does not make sense to call a viewController that immediately calls another viewController. Skip the loginTabController and just go straight to the userViewController. If for some reason you want to keep the code the loginTabController is using as a separate class that's fine just don't use a viewController subclass for it.

Show activity indicator during application launch

I am trying to add an activity indicator during startup. I did have a launch image, but I'd rather have just an indicator alone. I added the following to my app delegate, but the indicator doesn't appear.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// *******Create activity indicator****
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(225, 115, 30, 30)];
[activity setBackgroundColor:[UIColor clearColor]];
[activity setActivityIndicatorViewStyle: UIActivityIndicatorViewStyleGray];
[window addSubview: activity];
[activity release];
[activity startAnimating];
activity.hidden = FALSE;
// *******End activity indicator****
MainViewController *viewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
self.mainViewController = viewController;
[window addSubview:[mainViewController view]];
mainViewController.view.frame = CGRectMake(0, 20, 320, 411);
[window addSubview:[rootController view]];
[window makeKeyAndVisible];
#if !TARGET_IPHONE_SIMULATOR
[application registerForRemoteNotificationTypes:
UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
#endif
application.applicationIconBadgeNumber = 0;
// Hide indicator
[viewController release];
activity.hidden = TRUE;
[activity stopAnimating];
return YES;
}
I visited this question from back in 2009, but the solution didn't work for me.
iPhone-SDK:Activity Indicator during Startup?
For all those who needed this, and i know there were many...
(Made on Xcode version 4.2.1)
So...
In the AppDelegate.h add this:
#property (nonatomic, strong) UIImageView *splashView;
In the AppDelegate.m add this:
On the top of the page of cours #synthesize splashView;
And then:
- (void) splashFade
{
splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, 320, 480)];
splashView.image = [UIImage imageNamed:#"Default.png"];
[_window addSubview:splashView];
[_window bringSubviewToFront:splashView];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0];
[UIView setAnimationDelay:2.5];
[UIView setAnimationTransition:UIViewAnimationTransitionNone forView:_window cache:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(startupAnimationDone:finished:context:)];
splashView.alpha = 0.0;
[UIView commitAnimations];
//Create and add the Activity Indicator to splashView
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityIndicator.alpha = 1.0;
activityIndicator.center = CGPointMake(160, 360);
activityIndicator.hidesWhenStopped = NO;
[splashView addSubview:activityIndicator];
[activityIndicator startAnimating];
}
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
[splashView removeFromSuperview];
}
The [UIView setAnimationDelay:2.5] is responsible for how long the splashView will be in front by the delay time you choose.
You can change the position of the indicator by changing the nubmers of x/y in:
activityIndicator.center = CGPointMake(160, 360);
At last, under the methode:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Just add this:
[self performSelector:#selector(splashFade) withObject:nil];
And there you go :)
Hope it helped.
Have a nice programming....
I don't think this is possible from looking at the docs for UIApplicationDelegate
The earliest your app is called is in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Which as the name suggests is when your app hasFinishedLaunching
The reason nothing happens when you show and hide the indicator is to do with threading. The user interface will only update at the end of your method where the net result of your changes will be that the indicator is hidden.
Another solution is to let the launchimage load and when your ViewController loads at the viewDidLoad, use overlay + indicator on top of your view with alpha below 1:
UIView *baseView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.view addSubview:baseView];
[baseView setBackgroundColor:[UIColor blackColor]];
baseView.userInteractionEnabled = NO;
baseView.alpha = 0.4;
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
indicator.center = self.view.center;
[self.view addSubview:indicator];
[indicator bringSubviewToFront:self.view];
[indicator startAnimating];
Now comes the fun part, use delegate to remove the indicator + overlay when all your background threads finish to load up and simple call a method via your delegate:
- (void) onFinishLoading{
[indicator stopAnimating];
[baseView removeFromSuperview];
}
In order to stopAnimation and removeFromSuperView you might want to make baseView and indicator property + #synthesize
This isn't possible (as #Paul.S is saying). However, you can simulate this by adding a (static, so it's not animating) Default.png-splash-screen to your project.
In my opinion, though, you should rather invest in a better performance. For example: do you link your app with unused frameworks (eg. QuartzCore, MapKit) or something.
Starting an app should take 2 seconds max. At this time, you don't really need to show your user an activity-indicator. If it does take longer, you should take a look at what makes it so slow, rather than trying to hide it.

How to Animate a custom NSWindow

I'm having some troubles animating a custom NSWindow.
Here's my init method:
- (CustomWindow *)initWithView:(NSView *)view
{
if ((self = [super initWithContentRect:contentRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO])) {
[[self contentView] addSubview:view];
[self setBackgroundColor:[NSColor darkGrayColor]];
[self setMovableByWindowBackground:YES];
[self setExcludedFromWindowsMenu:YES];
[self setOpaque:NO];
[self setHasShadow:YES];
[self setDelegate:self];
}
return self;
}
When I call [self setFrame:originalFrame display:NO animate:YES]; there is a delay that corresponds to the animation duration, but the animation itself doesn't occur.
I think this is because I'm using a borderless window?
Simply use this: [self setFrame:originalFrame display:YES animate:YES];
Guess your window is resized, but view you add to content view, don't. If you have only one view, then instead of:
[[self contentView] addSubview:view];
use:
[self setContentView:view];

Cocoa App Enter Full Screen

I am developing a simple application which make use of full screen window.
Window contains view that contains button, image etc etc... , but when I enter in full screen with the follow lines:
NSWindow* tmp = [self window];
[tmp setStyleMask:NSBorderlessWindowMask];
[tmp setFrame:[tmp frameRectForContentRect:[[tmp screen] frame]]display:YES animate:NO];
[tmp setBackingType:NSBackingStoreBuffered];
screenRect = [[NSScreen mainScreen] frame];
int windowLevel = CGShieldingWindowLevel();
[self.window setLevel:windowLevel];
The view I put in the window doesn't resized automatically, I could make some operation for resize correctly that work, but there are a way to do that automatically?
I post all my AppController here:
-(id)init {
self = [super initWithWindowNibName:#"MainWindow"];
NSWindow* tmp = [self window];
[tmp setStyleMask:NSBorderlessWindowMask];
[tmp setFrame:[tmp frameRectForContentRect:[[tmp screen] frame]]display:YES animate:NO];
[tmp setBackingType:NSBackingStoreBuffered];
screenRect = [[NSScreen mainScreen] frame];
/**
// [[tmp standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
// [[tmp standardWindowButton:NSWindowZoomButton] setHidden:YES];
self.window = [[NSWindow alloc] initWithContentRect:screenRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO screen:[NSScreen mainScreen]];
**/
int windowLevel = CGShieldingWindowLevel();
[self.window setLevel:windowLevel];
return self;
}
// We need to be layer-backed to have subview transitions.
-(void)awakeFromNib {
[[self window] setContentSize:[topMenu frame].size];
[[[self window] contentView] addSubview:topMenu];
[topMenu enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
[[[self window] contentView] setWantsLayer:YES];
}
- (void)dealloc
{
[super dealloc];
}
- (void)windowDidLoad
{
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
[topMenu_controller performAnimation];
return;
}
You can use the springs and struts of Interface Builder to set a view's autosizing behavior:
http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/IB_UserGuide/Layout/Layout.html