Releasing view object - iphone

Just wondering why I'm getting the 'baseView' undeclared error in dealloc when building this.
CGRect baseFrame = CGRectMake(0, 0, 320, 480);
UIView *baseView = [[UIView alloc] initWithFrame:baseFrame];
self.view = baseView;
- (void)dealloc {
[baseView release];
[super dealloc];
}
I created the view with an alloc, I'm not sure why I'm getting the error when trying to release baseView. (I get the same error when trying to set it to nil in viewDidUnload.

Because "baseView" is not declared in the .h file would be my guess. The pointer only exists for the life cycle of the method in which it's declared.
You can fix this as follows:
CGRect baseFrame = CGRectMake(0, 0, 320, 480);
UIView *baseView = [[UIView alloc] initWithFrame:baseFrame];
[self.view addSubview:baseView];
[baseView release];
The view will retain the baseView, so you can go ahead and release it here. Then remove the reference in dealloc.

The baseView pointer is declared locally in whatever method you are creating it in. If you need to play around with baseView in other methods too, I suggest you add it as an instance variable.
// MyClass.h
#interface MyClass {
UIView *baseView; // declare as an instance variable;
}
#end
// MyClass.m
#import "MyClass.h"
#implementation MyClass
- (void)someMethod {
baseView = [[UIView alloc] initWithFrame:..];
}
- (void)someOtherMethod {
// baseView is accessible here
}
- (void)yetAnotherMethod {
// baseView is accessible here too
}
#end

try to use with
[self.view addSubView:baseView];
[baseView release];
If you want to release from dealloc , you need to declare in .h file

baseView is declared as a local variable and is known or can be accessed only by the method in which it is declared. If it has to be accessed by other methods of the class make sure baseView is declared as an instance variable.

Related

setNeedsDisplay does not always call drawRect

I've seen this question posted before but I cannot find the answer..
As expected, drawRect gets called automatically when the program launches. However when I call [self setNeedsDisplay] drawRect does not get called anymore and I cannot understand the reason why...
//DrawView.m
[self setNeedsDisplay];
-(void)drawRect:(CGRect)rect{
NSLog(# "Called_1");
//DRAW A PATH
}
Maybe using self is incorrect. I have also tried using DrawImage but still it doesn't work.
//DrawView.h
#interface DrawView : UIView {
UIImageView *drawImage;
}
//DrawView.m
-(id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
drawImage = [[UIImageView alloc] initWithImage:nil];
drawImage.frame = CGRectMake( 0, 0, self.frame.size.width, self.frame.size.height );
[self addSubview:drawImage];
}
return self;
}
Usually when you want to have a view redraw you should call:
[self setNeedsDisplay: YES];
Granted I have never build on iOS, on OSX this code works every time. Also, for example, if you want your delegate to call a redraw for a view named someView:
[someView setNeedsDisplay: YES];
Note: that YES is a macro defined by obj-c that is just a value of 1.

UIWebView appears null when calling fro a method

I have a major problem when trying to access a UIWebView that was created during ViewDidLoad, the UIWebView appears null
here is how i declare the property
#property (nonatomic, retain) UIWebView *detailsView;
the implementation
#implementation iPadMainViewController
#synthesize detailsView;
- (void)viewDidLoad
{
[super viewDidLoad];
detailsView = [[UIWebView alloc] initWithFrame:CGRectMake(500, 0, 512, 768)];
[self.view addSubView:detailsView];
}
When accessing from
- (void)loadDetailedContent:(NSString *)s
{
NSLog(#"%#", detailsView);
}
I get NULL, is it a normal behavior or am i doing something wrong?
here is the touchesBegan that is being called, from the views subclass that is being touched,
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
iPadMainViewController *mycontroller = [[iPadMainViewController alloc] init];
self.delegate = mycontroller;
[self.delegate loadDetailedContent:NewsId];
}
1st of all change you synthesize to
#synthesize detailsView = _detailsView
and allocate your uiwebvie
_detailsView = [[UIWebView alloc] initWithFrame:CGRectMake(500, 0, 512, 768)];
And try to push your ipadMainController in touchBegan to main screen and in vieDidLoad call loadDetailedContent
Are you sure that loadDetailedContent is not called before viewDidLoad? Set breakpoints in both methods and see which one gets hit first.
EDIT Since you updated the question with the code, it is easy to see why this problem happens. In touchesBegan, you instantiate the UIViewController and then call loadDetailedContent. This means that loadDetailedContent will be called before viewDidLoad.
viewDidLoad is called first when the view it controls has been created.

Manual positioning of titleLabel in a custom UIButton (using layoutSubviews)

i've create a UIButton subclass for my app and i need to manually reposition the titleLabel to be about 1/4 of the button's height. this post; http://forums.macrumors.com/showthread.php?t=763140 appears to directly address the same issue, and the solution is simple and perfectly understandable. however, i have failed to implement this because my override of layoutSubviews is never called. my button is static, so layoutSubviews only need be called the first time, but it never is. my code:
#interface MyButton : UIButton {}
#implementation MyButton
- (id)initWithFrame:(CGRect)frame
{
self = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[[self layer] setCornerRadius:14.0f];
[[self layer] setBorderWidth:3.0f];
[[self layer] setMasksToBounds:YES];
[self setBackgroundColor:[UIColor redColor]];
[self setFrame:frame];
return self;
}
- (void)layoutSubviews
{
NSLog(#"layout subs\n");
[super layoutSubviews];
}
#end
moving the label would be no problem, but layoutSubviews is never called. i've also tried adding layoutIfNeeded, but it made no difference. what's even weirder, is i've tried to call [self layoutSubviews] directly from the constructor but layoutSubviews is still not called!. i'm starting to think this might even be a bug in SDK 3.1.3
can anyone help?!
In layoutSubviews, you can do layout changes to your subviews. Make sure that inside the method:
√ Call [super layoutSubviews]
x Do not change value or rect of self.frame here. If you do so, it would recursively call layoutSubviews.
√ Make sure you only change frame of its subviews.
From UIView Class Reference
to answer my own question, it seems i was subclassing incorrectly. taking the following approach results in layoutSubviews being called correctly:
+ (id)buttonWithFrame:(CGRect)frame {
return [[[self alloc] initWithFrame:frame] autorelease];
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame])
{
...
[self setFrame:frame];
}
return self;
}
hope that helps others

iphone using cocos2d get EXC_BAD_ACCESS

I am making a simple game project involving the use of Cocos2d. Now as defined by Ray Wenderlich's example, i have completed the whole tutorial but added an extra bit of code myself to check total number of melons, when they reach 3, i replace screen with "You Win" screen to notify the user that he has won using [[CCDirector sharedDirector] replaceScene:gameoverscreen];.
The problem is that i get EXC_BAD_ACCESS everytime i call this from ccTouchEnded coz my condition is checked here. But the same thing works if i use [[CCDirector sharedDirector] pushScene:gameoverscreen];
Cant understand what the problem is!!
the code for gameoverscreen screen is:
#import "GameOverScene.h"
#import "HelloWorldScene.h"
#implementation GameOverScene
#synthesize _layer = layer;
- (id)init {
if ((self = [super init])) {
self._layer = [GameOverLayer node];
[self addChild:layer];
}
return self;
}
- (void)dealloc {
[layer release];
layer = nil;
[super dealloc];
}
#end
#implementation GameOverLayer
#synthesize _label = label;
-(id) init
{
if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
self._label = [CCLabel labelWithString:#"" fontName:#"Arial" fontSize:32];
label.color = ccc3(0,0,0);
label.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:label];
[self runAction:[CCSequence actions:
[CCDelayTime actionWithDuration:3],
[CCCallFunc actionWithTarget:self selector:#selector(gameOverDone)],
nil]];
}
return self;
}
- (void)gameOverDone {
[[CCDirector sharedDirector] replaceScene:[[[HelloWorld alloc] init] autorelease]];
}
- (void)dealloc {
[label release];
label = nil;
[super dealloc];
}
#end
and the Header file of GameoverScene contains the following!
#import "cocos2d.h"
#interface GameOverLayer : CCColorLayer {
CCLabel *label;
}
#property (nonatomic, retain) CCLabel *_label;
#end
#interface GameOverScene : CCScene {
GameOverLayer *layer;
}
#property (nonatomic, retain) GameOverLayer *_layer;
#end
i call the scene from HelloWorld class using the following syntax!
GameOverScene *gameoverscene = [GameOverScene node];
[gameoverscene._layer._label setString:#"You WON!"];
[[CCDirector sharedDirector] pushScene:gameoverscene];
I see several issues in your code.
One is the CCLabel object, you initialize it as autorelease object using cocos2d's static initializer:
self._label = [CCLabel labelWithString:#"" fontName:#"Arial" fontSize:32];
But in the dealloc method you release it even though its an autorelease object:
- (void)dealloc {
[label release];
label = nil;
[super dealloc];
}
You should not release the label since it is set to autorelease by cocos2d! This is a guaranteed crash!
Then you make things more complicated than needed:
[[CCDirector sharedDirector] replaceScene:[[[HelloWorld alloc] init] autorelease]];
The alloc/init/autorelease is completely superfluous because you can simply write [HelloWorld scene] if the HelloWorld class has a +(id) scene method (it normally should). If not, then use [HelloWorld node]. Always prefer cocos2d's static autorelease initializers before using alloc/release on cocos2d objects. The only time you ever need to alloc a cocos2d class is when you explicitly don't add it as a child to some other node, which is rare.
Finally, this is very bad style:
-(id) init
{
if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {
If the super implementation of initWithColor calls [self init] - which is often the case and even if not, could change with future releases of cocos2d - it would call your implementation of init, resulting in an endless loop (stack overflow). To fix this simply either rename your init method or call [super init] and provide the parameters some other way, usually there will be a property or setter method to do so.
And a minor issue: Apple advises against using leading underscores as member variable prefix. In fact, many other compiler vendors advice against that too since often system internal variables use one or two underscores as prefix. The cocos2d style with trailing underscores is preferred which would have you write label_ instead of _label.
EXEC_BAD_ACCESS means you are using data that has been released. Does the youwin scene use data from the current scene? If so, it needs to retain the data. When replaceScene: is called, the current scene is not held in memory but when pushScene: is called, both scenes remain in memory.
EDIT:
Let's say you have two scenes, A and B. When you call pushScene:, A continues to exist in memory and B is added. When you call replaceScene:, A is removed and no longer exists, only the B scene. That is why A's data would disappear, but only when replacing.
The general rule when it comes to memory dealing is to release whatever you alloced or retained. In your case you are instantiating a CCLabel object with a convenience method (thus, not calling alloc) and you are not retaining it. So, that [label release] in your dealloc method must not be there in this case.
I also account with such thing,the reason for it may be you release something that are autorelease,so you can try it again by not to release some object in the dealloc method!

"Swapping" a UIView Instance variable - cannot dealloc "previous" view

I want to organize somehow my iPhone game's level-views, but I simply cannot (without expanding Object Allocations). I made a really "skeleton" of my code (this game has 2 levels, the goal is to release the iPhone display). I just cannot dealloc the previous level, so Instrunments shows incrementing BGTangramLevel instances.
Please, take a look on it, I need some helpful ideas on designing (my 3rd question on it).
viewcontroller.h
#interface compactTangramViewController : UIViewController
{
//The level.
BGTangramLevel *currentLevel;
UIColor *levelColor;
}
//It is to be just a reference, therefore I use assign here.
#property (nonatomic, retain) BGTangramLevel *currentLevel;
-(void) notificationHandler: (NSNotification*) notification;
-(void) finishedCurrentLevel;
#end
viewcontroller.m
#implementation compactTangramViewController
#synthesize currentLevel;
//Initializer functions, setting up view hierarchy.
-(void) viewDidLoad
{
//Set up levelstepper.
levelColor = [UIColor greenColor];
//Set up "state" classes.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationHandler:) name:#"finishedCurrentLevel" object:nil];
//Attach level 1.
currentLevel = [BGTangramLevel levelWithColor: levelColor frame:self.view.frame];
[self.view addSubview:currentLevel];
[super viewDidLoad];
}
//Release objects.
-(void) dealloc
{
[currentLevel release];
[super dealloc];
}
//Notification handling.
-(void) notificationHandler: (NSNotification*) notification
{
//Execute level swap.
if ([notification name] == #"finishedCurrentLevel") [self finishedCurrentLevel];
}
-(void) finishedCurrentLevel
{
//Remove previous level.
[currentLevel removeFromSuperview];
//[currentLevel release];
//Step level.
if (levelColor == [UIColor greenColor]) levelColor = [UIColor blueColor]; else levelColor = [UIColor greenColor];
//Attach level 2.
currentLevel = [BGTangramLevel levelWithColor: levelColor frame:self.view.frame];
[self.view addSubview:currentLevel];
}
#end
BGTangramLevel.h
#interface BGTangramLevel : UIView
{
BOOL puzzleCompleted;
}
//Initializer.
+(BGTangramLevel*)levelWithColor: (UIColor*) color frame: (CGRect) frame;
//Test if the puzzle is completed.
-(void) isSolved;
#end
BGTangramLevel.m
#implementation BGTangramLevel
//Allocated instance.
+(BGTangramLevel*)levelWithColor: (UIColor*) color frame: (CGRect) frame
{
BGTangramLevel *allocatedLevel = [[BGTangramLevel alloc] initWithFrame:frame];
allocatedLevel.backgroundColor = color;
return allocatedLevel;
}
//Finger released.
-(void) touchesEnded: (NSSet*)touches withEvent: (UIEvent*)event
{
//The completement condition is a simple released tap for now...
puzzleCompleted = YES;
[self isSolved];
}
//Test if the puzzle is completed.
-(void) isSolved
{
//"Notify" viewController if puzzle has solved.
if (puzzleCompleted) [[NSNotificationCenter defaultCenter] postNotificationName:#"finishedCurrentLevel" object:nil];
}
-(void) dealloc
{
NSLog(#"Will ever level dealloc invoked."); //It is not.
[super dealloc];
}
#end
So what should I do? I tried to mark autorelease the returning level instance, release currentLevel after removeFromSuperview, tried currentLevel property synthesized in (nonatomic, assign) way, but Object Allocations still grow. May I avoid Notifications? I'm stuck.
You need to follow retain/release rules more closely. You definitely should not experimentally add retain and release and autorelease in places just to find something that works. There's plenty written about Cocoa memory management already, I won't repeat it here.
Specifically, BGTangramLevel's levelWithColor:frame: method should be calling [allocatedLevel autorelease] before returning allocatedLevel to its caller. It doesn't own the object, it's up to the caller to retain it.
You also need to know the difference between accessing an instance variable and accessing a property. Cocoa's properties are just syntactic-sugar for getter and setter methods. When you reference currentLevel in your view controller you are dealing with the instance variable directly. When you reference self.currentLevel you are dealing with the property.
Even though you've declared a property, currentLevel = [BGTangram ...] simply copies a reference into the variable. In viewDidLoad, you need to use self.currentLevel = [BGTangram ...] if you want to go through the property's setter method, which will retain the object (because you declared the property that way). See the difference?
I think your leak is happening in finishedCurrentLevel. If you had used self.currentLevel = [BGTangram ...], the property's setter method would be called, which would release the old object and retain the new one. Because you assign to the instance variable directly, you simply overwrite the reference to the old level without releasing it.
Calling [currentLevel release] in the dealloc method of your view controller is correct.