releasing a retained property thats been autoreleased? - iphone

Hey all. Ive got a headache trying to get my head around this. I have a retained property of type NSNumber. When I use this property I instantiate it with an autoreleased NSNumber. When dealloc is called I then get bad access telling me that Im releasing something that has already been released. Heres some code.
#interface RadarAnnotation : NSObject <MKAnnotation> {
}
#property (retain, nonatomic) NSNumber *latitude;
#end
#implementation RadarAnnotation
#synthesize latitude;
- (CLLocationCoordinate2D)coordinate
{
coordinate.latitude = [self.latitude doubleValue];
return coordinate;
}
-(void) dealloc {
[super dealloc];
[latitude release];//error is here when mapViewController is popped off stack.
}
Here is how I instantiate the property in my mapViewController :
poi.latitude = [NSNumber numberWithDouble:map.centerCoordinate.latitude];
What am I doing wrong? Many thanks. Jules.

You should be calling [super dealloc] at the end of the dealloc method.

Related

NSDictionary setting to nil when passed to another class (IOS)

I am passing an NSDictionary object from one view class to another as I transition from a table view to a normal view to show details:
Passing Controller:
[tweetController setTweet:tweet];
Receiving Controller.h:
#interface TweetViewController : UIViewController {
NSDictionary *tweet;
}
#property (nonatomic, retain) NSDictionary *tweet;
Receiving Controller.m:
#implementation TweetViewController
#synthesize tweet = _tweet;
I then try to use this information to set the properties of some fields in my view:
- (void)viewDidLoad
{
[super viewDidLoad];
tweetLabel.text = [_tweet objectForKey:#"text"];
}
The result is a blank label and if I inspect the value of _tweet at this stage it is nil.
I originally had a method which set the value of tweet which I called at the same location as I am now setting the value. If I inspected the value at this stage it was fine.
I presume that the automagic setter through #synthasize is working, but somewhere else the value is being lost.
Sorry this is my first objective C anything! Thanks for any help in advance.
You are using your "tweet" instance variable, whereas the "tweet" property is synthesized to the "_tweet" variable.
You are probably calling the setTweet method after viewDidLoad executes.
I usually pass this kind of thing into a custom init method.
Alternatively, you could do the set before pushing the detail VC onto the nav stack.
Are you sure that tweetLabel isn't nil?
I've made a few corrections & optimisations to your code. You don't need to declare ivars in the header file anymore, they are generated automatically by #synthesize
- (void)dealloc; is only needed if you're not using ARC.
//.h
#interface TweetViewController : UIViewController
#property (strong, nonatomic) NSDictionary *tweet;
#property (strong, nonatomic) IBOutlet UILabel *tweetLabel
#end
//.m
#implementation TweetViewController
#synthesize tweet = _tweet;
#synthesize tweetLabel = _tweetLabel;
- (void)viewDidLoad {
[super viewDidLoad];
self.tweetLabel.text = [self.tweet objectForKey:#"text"];
}
- (void)dealloc {
[_tweet release];
[_tweetLabel release];
[super dealloc];
}
#end
Note: strong is equivalent to retain
To expand on #Rayfleck's answer, since you are new to Objective-C, your custom init method could look like this:
In TweetViewController.h:
- (id)initWithTweet:(NSDictionary*)tweet;
In TweetViewController.m:
- (id)initWithTweet:(NSDictionary*)tweet
{
self = [super init];
if (self) {
_tweet = tweet;
}
return self;
}
and then in your passing controller you'd allocate and initialize like this:
TweetViewController *tvc = [[TweetViewController alloc] initWithTweet:myTweet];

#synthesize interferes with inheritance

FINAL EDIT
The solution to all life's problems:
Switching xcode's "Compiler Version" setting to "LLVM compiler 2.0" solves this issue, many thanks to Firoze Lafeer for the concerted, constructive assistance!
Intention was to build some really basic functionality into all my classes by subclassing NSObject and UIViewController with something to grab the application delegate, extend the viewDidAppear mechanism a little, etc. I've got a base class that looks something like this (only relevant lines included):
#interface PHView : UIViewController {
id<PHAppDelegate> appDelegate;
}
-(id)init;
//some other method prototypes
#property (nonatomic, retain) id delegate;
#end
#implementation PHView
#synthesize delegate;
-(id)init {
self = [super init];
appDelegate = (id<PHAppDelegate>)[[UIApplication sharedApplication] delegate];
visible = FALSE;
initialized = FALSE;
return self;
}
//some other methods
#end
EDIT I should mention here that the property "delegate" isn't meant to point to ivar "appDelegate" or anything... I only left it in to illustrate that this superclass uses #synthesize. Since it's not a related useage I think it doesn't matter, but I wouldn't say that I know that.
Interface for the subclass:
#interface PinMap : PHView <MKMapViewDelegate> {
//#interface PinMap : UIViewController <MKMapViewDelegate> {
// NSObject<PHAppDelegate>* appDelegate;
}
-(id)init;
-(void)zoomToUser;
#property (nonatomic, retain) IBOutlet MKMapView* map;
#end
This compiles:
#implementation PinMap
//#synthesize map;
-(PinMap*) init{
self = [super init];
//appDelegate = (NSObject<PHAppDelegate>*)[[UIApplication sharedApplication] delegate];
return self;
}
-(void)zoomToUser {
//MKCoordinateRegion region = map.region;
MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake((CLLocationDegrees)300, (CLLocationDegrees)300), MKCoordinateSpanMake((CLLocationDegrees)20,(CLLocationDegrees)20)); //random region
region.center = [[appDelegate location] coordinate];
region.span.longitudeDelta /= 50.0;
region.span.latitudeDelta /= 50.0;
// [map setRegion:region animated:NO];
}
This does not compile:
#implementation PinMap
#synthesize map;
-(PinMap*) init{
self = [super init];
//appDelegate = (NSObject<PHAppDelegate>*)[[UIApplication sharedApplication] delegate];
return self;
}
-(void)zoomToUser {
MKCoordinateRegion region = map.region;
//MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake((CLLocationDegrees)300, (CLLocationDegrees)300), MKCoordinateSpanMake((CLLocationDegrees)20,(CLLocationDegrees)20)); //random region
region.center = [[appDelegate location] coordinate]; // <-- ERROR HERE
region.span.longitudeDelta /= 50.0;
region.span.latitudeDelta /= 50.0;
[map setRegion:region animated:NO];
}
At the marked location, I get """'appDelegate' undeclared (first use in this function)""" My first step was to Clean, reboot and Clean again (fixed three bugs this week using that procedure) and when that didn't work I started trying things that make sense, and eventually some things that DON'T make sense.
The following DOES compile (and run) but I honestly don't understand why:
#interface PinMap : PHView <MKMapViewDelegate> {
//#interface PinMap : UIViewController <MKMapViewDelegate> {
//NSObject<PHAppDelegate>* appDelegate;
MKMapView* _map;
}
-(id)init;
-(void)zoomToUser;
#property (nonatomic, retain) IBOutlet MKMapView* map;
#end
#implementation PinMap
#synthesize map=_map;
-(PinMap*) init{
self = [super init];
//appDelegate = (NSObject<PHAppDelegate>*)[[UIApplication sharedApplication] delegate];
return self;
}
-(void)zoomToUser {
MKCoordinateRegion region = self.map.region;
//MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake((CLLocationDegrees)300, (CLLocationDegrees)300), MKCoordinateSpanMake((CLLocationDegrees)20,(CLLocationDegrees)20)); //random region
region.center = [[appDelegate location] coordinate];
region.span.longitudeDelta /= 50.0;
region.span.latitudeDelta /= 50.0;
[self.map setRegion:region animated:NO];
}
In this trim it'll respond to "self.map" but considers "map" to be undeclared.
EDIT The "self" requirement makes sense to me now, but the disappearing / reappearing "appDelegate" ivar is what I'm actually worried about. Sorry if that was unclear before. But seriously, what's up with that?
Well, for starters, PinMap doesn't have an instance variable (ivar) called 'map'.
It has a property called 'map', which you can access like so:
region = self.map.region;
Or it has an ivar called _map. So you could do this:
region = _map.region;
But the former is recommended. You made a property, so you probably want to use it (outside of initXXX and dealloc)
EDIT
Also, the designated initializer for UIViewController is initWithNibName:bundle:
So just make sure if you subclass UIViewController that you call its designated initializer.
EDIT AGAIN
In those cases in your comment, you probably have an ivar and a property with the same name. If you just do #synthesize propname, that's what you get. But in this case you did #synthesize map = _map.
It's worth taking the time to understand when you are accessing the ivar directly, versus the property. Otherwise a lot of things won't make sense, and other bugs will happen. To access the property you must do 'self.propertyName' (or [self propertyName] or [self setPropertyName:something])
If you aren't using self, you aren't using the getter/setter (this is often a bad thing, if for example your getter/setter is doing your memory mgmt or initialization for you). You also have to use the actual ivar name if you aren't going to use the property.
EDIT AGAIN AGAIN
I see changing the compiler helped. I would suggest two things then: double check all of your #synthesize statements to make sure you aren't asking the compiler to synthesize an ivar in a subclass that already exists in a superclass. While you're sorting that out, I would recommend you name your ivars as variable_ or _variable so you can easily see where you are using the ivar versus the property.
And more importantly, you should really upgrade to LLVM 3.0, which is included in Xcode 4.2.1.
#synthesize creates the setter and getter methods for a property, however it needs some place to store that object so you must add some sort of ivar to use #synthesize with a property
you can do
#interface ... : ... {
Something* smth;
}
#property (nonatomic, retain) Something *smth;
#end
#implementation ...
#synthesize smth;
#end
or
#interface ... : ... {
Something* _smth;
}
#property (nonatomic, retain) Something *smth;
#end
#implementation ...
#synthesize smth=_smth;
#end

MKMapView removeAnnotations cause memory leak

everyone!
I have tested this simplest code as following:
StorePin.h
#import <Foundation/Foundation.h>
#import <MAPKIT/mapkit.h>
#import <CORELOCATION/corelocation.h>
#interface StorePin : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *subtitle;
NSString *title;
}
#property (nonatomic,assign) CLLocationCoordinate2D coordinate;
#property (nonatomic,retain) NSString *subtitle;
#property (nonatomic,retain) NSString *title;
-(id) initWithCoords:(CLLocationCoordinate2D) coords;
#end
StorePin.m
#import "StorePin.h"
#implementation StorePin
#synthesize coordinate, subtitle, title;
- (id) initWithCoords:(CLLocationCoordinate2D) coords{
self = [super init];
if (self != nil) {
coordinate = coords;
}
return self;
}
- (void) dealloc
{
[title release];
[subtitle release];
[super dealloc];
}
#end
In my ViewControlller, I made a button to add and remove annotations repeatly.
#import "mapViewTestViewController.h"
#import "StorePin.h"
#implementation mapViewTestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)refresh
{
[mapView removeAnnotations:mapView.annotations];
for (int i = 0; i < 101; i ++)
{
CLLocationCoordinate2D p1;
p1.latitude = i/10.0;
p1.longitude = i/10.0;
StorePin *poi = [[StorePin alloc] initWithCoords:p1];
[mapView addAnnotation:poi];
[poi release];
}
}
- (void)dealloc
{
[super dealloc];
}
#end
If I loop less than 100 times to add and remove annotations, all work normally. But if I loop more than 100 times, it will cause memory leak once. I'm nearly crazy to this strange problem. Is this my code's bug or mkmapview's bug? Thank you for helping me.
You don't say what objects have been detected as leaking, but if they are StorePins, then it's MapKit's problem -- your memory management code for the StorePins you create in the loop is just fine.
One thing that you do that might be causing MapKit trouble is passing the map view a reference to its own ivar that you want it to modify. It doesn't seem too likely -- if it was really a problem, it would probably cause a crash rather than a leak. However, you might try making a copy, either shallow (as Kai wrote earlier, but absolutely do not follow the advice about using retain counts and calling release in a loop):
NSArray * annotationsCopy = [NSArray arrayWithArray:mapView.annotations];
or deep:
NSArray * annotationsDeepCopy = [[[NSArray alloc] initWithArray:mapView.annotations
copyItems:YES]
autorelease];
then pass the copy to removeAnnotations:.
The second option creates an autoreleased array with a copy of every item in the annotations list so that the map view doesn't try to remove the same instances that it's iterating over. Obviously this uses twice the memory; you probably only want to bother with this for bug-hunting.
If it fixes the leak, great, if not, then there's probably nothing you can do about it.
In case you don't want to remove the user's location blue dot on the map, you can use:
NSArray * annotationsCopy = [NSArray arrayWithArray:[mapView.annotations filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"!(self isKindOfClass: %#)", [MKUserLocation class]]]];

Setting a object with "Assign" property to nil

If I am having a variable defined with "Assign" property then is it ok to setting them to nil in dealloc method?
#property (nonatomic, assign) id test;
- (void)dealloc {
self.test = nil;
}
It's better to release the ivar directly. If a subclass overrides the setter methods of a property, your object might leak because your setter is not called. Consider:
#interface ClassA
#property (readwrite, retain) id anObject;
#end
#interface ClassB : ClassA
#end
#implementation ClassA
#synthesize anObject;
- (void)dealloc {
self.anObject = nil;
[super dealloc];
}
#end
#implementation ClassB
- (void)setAnObject: (id)anObject {
// do nothing!
}
#end
Instances of ClassB will leak anObject!
Depends how you do it, if you do it via the property setter (not recommended), then yes.
If you do direct assignment, then no, because the retained object will leak.
So this is fine:
- (void) dealloc {
self.test = nil;
[super dealloc];
}
But this is a no-go:
- (void) dealloc {
test = nil;
[super dealloc];
}
My advice is to just send the release message to all of your retained ivars in -dealloc, this will work nicely because if test happens to be nil, then nothing will happen.
Trust me. Send release directly in -dealloc. That is all.
- (void) dealloc {
[test release];
[super dealloc];
}

How can I simply change a class variable from another class in ObjectiveC?

I simply want to change a variable of an object from another class. I can compile without a problem, but my variable always is set to 'null'.
I used the following code:
Object.h:
#interface Object : NSObject {
//...
NSString *color;
//...
}
#property(nonatomic, retain) NSString* color;
+ (id)Object;
- (void)setColor:(NSString*)col;
- (NSString*)getColor;
#end
Object.m:
+(id)Object{
return [[[Object alloc] init] autorelease];
}
- (void)setColor:(NSString*)col {
self.color = col;
}
- (NSString*)getColor {
return self.color;
}
MyViewController.h
#import "Object.h"
#interface ClassesTestViewController : UIViewController {
Object *myObject;
UILabel *label1;
}
#property UILabel *label1;
#property (assign) Object *myObject;
#end
MyViewController.m:
#import "Object.h"
#implementation MyViewController
#synthesize myObject;
- (void)viewDidLoad {
[myObject setColor:#"red"];
NSLog(#"Color = %#", [myObject getColor]);
[super viewDidLoad];
}
The NSLog message is always Color = (null)
I tried many different ways to solve this problem, but no success.
Any help would be appreciated.
Thanks for the help so far.
I modified the code as follow, but it still doesn't work as it should.
MyViewController.h:
#import <UIKit/UIKit.h>
#import "Object.h"
#interface MyViewController : UIViewController {
Object *myObject;
}
#property (nonatomic, retain) Object *myObject;
#end
MyViewController.m:
#import "MyViewController.h"
#import "Object.h"
#implementation MyViewController
#synthesize myObject;
- (void)viewDidLoad {
Object *myObject = [Object new];
myObject = 0;
[myObject setColor:#"red"];
NSLog(#"color = %#", myObject.color);
[super viewDidLoad];
}
If I do it like this, NSLog returns color = null (and I think myObject is only visible in viewDidLoad). How can declare myObject and make it visible in MyViewController?
I stripped down my Object class to
Object.h:
#interface Object : NSObject {
NSString *color;
}
#property(nonatomic, retain) NSString *color;
#end
Object.m:
#import "Object.h"
#implementation Object
#synthesize color;
#end
I wasn't able to define an object myObject in ViewDidLoad so that I can access its properties from the whole ViewController class? What did I miss?
Side question: Why do I have to set myObject to 0?
You're declaring a property, then explicitly declaring the accessors in Object.h. You only need to do one or the other - they mean the same thing (well, almost - you'll have color instead of getColor)
To implement the property in Object.m you should use #synthesize color. The explicit implementations, again, are then redundant (unless they do anything extra).
The explicit setColor implementation in Object.m is calling the property - which you are implementing explicitly, so I would have expected you to get an infinite recursion here.
MyViewController.m should probably synthesize label1, since you declare the property in the header (although it's not being used in your snippet).
[myObject getColor] is calling the color property, which you declared but did not synthesize. If you had explicitly implemented it as color it would have picked that up - but it won't match getColor (which is fortunately as that would have led to an infinite recursion again.
I don't see anywhere where you create your myObject instance. If you don't it will be nil and methods called on it (including property accesses) will return 0 or nil.
I suspect (6) is the cause of your issue, but the others need to be addressed too. Make sure you read up on property syntax.