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
Related
I have seen that a few others have had this problem as well.
I'm trying to follow a tutorial online that shows how to create animated pins on a MapView.
I have implemented the code as shown in the tutorial and the project builds fine except I receive this exception:
-[MKPointAnnotation iconN]: unrecognized selector sent to instance
I have a subclass of 'MKPinAnnotationView' and in the .m file I create this method:
- (void)setAnnotation:(id<MKAnnotation>)annotation {
[super setAnnotation:annotation];
//Place *place = [[Place alloc] init];
Place *place = (Place *)annotation;
//The following line is where the program sends "SIGABRT"
icon = [UIImage imageNamed:[NSString stringWithFormat:#"pin_%d.png", [place.iconN intValue]]];
[iconView setImage:icon];
}
Here are a few parts from my "model" which is called Place.h/.m.
Here is where I create the property for 'iconN'.
#property (retain, nonatomic) NSNumber *iconN;
And here I synthesize it:
#synthesize iconN = _iconN;
Any help is greatly appreciated.
EDIT:
Here is the Place.h and Place.m
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#interface Place : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
}
#property (retain, nonatomic) NSNumber *iconN;
#property (nonatomic, copy) NSString *title;
#property (nonatomic) CLLocationCoordinate2D coordinate;
- (id)initWithLong:(CGFloat)lon Lat:(CGFloat)lat iconNumber:(NSNumber *)iconNumber;
#end
And the Place.m
#import "Place.h"
#implementation Place
#synthesize coordinate;
#synthesize iconN = _iconN;
#synthesize title;
- (id)initWithLong:(CGFloat)lon Lat:(CGFloat)lat iconNumber:(NSNumber *)iconNumber {
self = [super init];
if (self) {
coordinate = CLLocationCoordinate2DMake(lat, lon);
self.iconN = iconNumber;
}
return self;
}
- (NSString *)title {
return [NSString stringWithFormat:#"Bus: %d", [self.iconN intValue]];
}
- (NSString *)subtitle {
return [NSString stringWithFormat:#"bus[%d] from database.", [self.iconN intValue] - 1];
}
#end
You cannot convert a MKAnnotation to a Place just by casting it. This line is wrong.
Place *place = (Place *)annotation;
You should post your Place.h and Place.m files if you're still stuck. You need to either set the iconN property on a new Place object, or create an init method in the Place class that accepts the MKAnnotation object as a parameter and sets it own internal values accordingly.
In the line
Place *place = (Place *)annotation;
has the variable place of annotation variable class (MKPointAnnotation), you are not able to bring the master class variable to a subclass in this way. Instead you'll have to make a constructor for Place from MKPointAnnotation and perform a check in the setAnnotation method that annotation is of MKPointAnnotation.
You are sending the message to the annotation but you seem to have subclasses the annotation view.
Posting as an answer what was originally just a comment:
I'm not familiar with the MapKit, but the thing that sticks out for me in this: -[MKPointAnnotation iconN]: unrecognized selector sent to instance is that the class is MKPointAnnotation. So the annotation you're receiving isn't actually a Place object, it's an MKPointAnnotation object - you can't just cast to Place. I suspect the root of your problem is where you create your annotation object in the first place.
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];
Sorry if the question is not to clear, this is what im trying to do. I have an object, a delegate and a view controller.
Object pseudo-code
#interface Car: NSObject {
UIColor *color;
}
#property (assign)UIColor *color;
- (void) setColor(UIColor col);
#end
#implementation Car
#synthetize color;
// i know that synthesize makes this function redundant.
// I just used it to demonstrate
// the need to access an instance method.
- (void) setColor(UIColor col)
{
color = col;
}
#end
delegate code
#interface myDelegate: UIApplicationDelegate {
Car *car;
UIViewController *theView;
}
#property (assign)Car *car;
#end
#implementation myDelegate
#synthesize car;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
theView = [[MyViewController alloc]init];
return YES;
}
#end
View pseudo Code
#interface MyViewController: UIViewController {
MyDelegate *appDelegate;
}
#property (retain) MyDelegate *appDelegate;
#end
#implementation MyViewController
#synthesize appDelegate;
- (void)viewDidLoad {
self.appDelegate = (MyDelegate*)[[UIApplication sharedApplication] delegate];
/// THIS IS MY ISSUSE!
[self.appDelegate.car setColor(UIColor);
}
#end
Can anyone explain or point me to where i can understand why [self.appDelegate.car setColor()] gives me a compile error that reads "Unknown component setColor of a property".
Im sure there is a way to do this in objective C as i would do it in python, java or other OO language.
Any help would be greatly appreciated
Cheers
Rudy
You are not using UIColor as a pointer.
Try using UIColor * instead of just UIColor and the compiler will stop complaining
First of all, the Car class has problems. The color property should be defined as retain, not assign. assign is generally for non-object type properties, such as an NSInteger, BOOL, int, etc., and for a special case of objects that shouldn’t be retained because they’d create retain cycles. Also, - (void)setColor(UIColor col); is not a valid method name, as it is written as if it were a function. In Objective-C, each parameter is preceded by a colon :.
For example, take the following method:
- (void)setBodyColor:bodyColor lowerColor:lowerColor upperColor:upperColor;
While that is technically a valid method signature, it is generally written differently to make its usage more clear. As it’s defined above, each parameter is of type id, which is a generic object type. To make things clearer, you cast each argument to the type of objects they represent:
- (void)setBodyColor:(UIColor *)bodyColor
lowerColor:(UIColor *)lowerColor
upperColor:(UIColor *)upperColor;
In addition to being defined incorrectly, it’s also superfluous since defining a read-write property named color implies that a -setColor: method will be defined. The code would look like this:
#interface Car: NSObject {
UIColor *color;
}
#property (retain) UIColor *color;
#end
#implementation Car
#synthetize color;
- (void)dealloc {
[color release];
[super dealloc];
}
// If you need to override a method, that’s fine
- (void) setColor:(UIColor *)aColor
{
[aColor retain];
[color release];
color = aColor;
// do more stuff
}
#end
On to your delegate, it also has problems. First, myDelegate is defined as a subclass of UIApplicationDelegate, which is not even a class: it’s a protocol (or interface) that other objects can conform to. The car property should also be defined as retain, since it’s an object that your app delegate owns. Here, theView (which should likely be renamed to something like theViewController) should be typed as MyViewController to make it more clear.
#interface MyDelegate : NSObject <UIApplicationDelegate> {
Car *car;
MyViewController *theView;
}
#property (retain) Car *car;
#end
#implementation MyDelegate
#synthesize car;
- (void)dealloc {
[car release];
[theView release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
theView = [[MyViewController alloc] init];
return YES;
}
#end
The MyViewController class has problems in that the appDelegate is defined as retain when it should likely be assign. The app delegate itself is creating the view controller using alloc/init, meaning the app delegate “owns” the view controller. The view controller shouldn’t retain the app delegate because that would create a retain cycle (see Retain Cycles).
MyViewController.h
// forward declaration
#class MyDelegate;
#interface MyViewController: UIViewController {
MyDelegate *appDelegate; // non-retained
}
#property (assign) MyDelegate *appDelegate;
#end
MyViewController.m
#import "MyViewController.h"
#import "MyDelegate.h"
#implementation MyViewController
#synthesize appDelegate;
- (void)viewDidLoad {
self.appDelegate = (MyDelegate*)[[UIApplication sharedApplication] delegate];
[self.appDelegate.car setColor:[UIColor clearColor]];
/// THIS IS MY ISSUSE!
// [self.appDelegate.car setColor(UIColor);
}
#end
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]]]];
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.