NSLog returning null instead of string - objective-c

NSLog is returning the output 'Null" instead of a string that I would have expected. I suspect that this is a problem with private instance variables and such, but since I am not familiar with Object-oriented programming I cannot determine the cause.
//The viewDidLoad method in MainGameDisplay.m:
- (void)viewDidLoad
{
[super viewDidLoad];
Engine *engine = [[Engine alloc] init];
[engine setPlayerName: viewController];
}
The string is entered by a UITextField, the property being
//ViewController.h
#property (strong, nonatomic) IBOutlet UITextField *PlayerNameTextView;
The method works fine and returns the correct string if [engine setPlayerName: self] is placed into ViewController, but anywhere outside the location that *PlayerNameTextView is causes this problem.
//Engine.m
#implementation Engine
{
ViewController *firstPage;
}
NSString *Player;
-(void) setPlayerName: (ViewController *) name
{
Player = [[name PlayerNameTextView] text];
NSLog(#"%#", Player);
}

NSLog return type is void as you can see in it's documentation. There is no reason to expect any return value for a call to it, since it does not return anything.

Make sure that 'name' is properly initialized. Try putting an assert(name != nil) right before the NSLog. Or better yet, set a breakpoint at the NSLog and inspect the variables.
Another suggestion: Why not make the method -(void) setPlayerName:(NSString*)name? This is more straightforward than passing around pointers to view controllers, and would be easier to debug.

Related

Why doesn't this property update in UI?

I have a simple app: A text box bound to item.title. This property is exposed as a NSString, but internally is a NSMutableString.
The problem: Changes to the title property are not reflected in the UI.
Here is the implementation of item:
#interface Item : NSObject
{
NSMutableString *_title;
}
#property NSString *title;
#end
And the implementation:
#implementation Item
-(void)mutateTitle
{
[self willChangeValueForKey:#"title"];
[_title appendString:#"mutated"];// this does not work
//_title = [[NSMutableString alloc] initWithString:#"new instance"];// this works
[self didChangeValueForKey:#"title"];
}
-(NSString *)title
{
NSLog(#"get title returning: %#", _title);
return _title;
}
-(void)setTitle:(NSString *)title
{
_title = [[NSMutableString alloc] initWithString:title];
}
#end
When a button is pressed, we send mutateTitle to the item, which changes the title, and notifies KVO of the property change.
The UI does respond to the change, and when title is called, we return the correct string.
But the UI does not reflect the changes.
Note that if I un-comment the line where I assign a new instance to _title, the update happens fine.
Also, if title returns a copy of _title, it also works fine.
Why is this happening? Is there a way I can make this work as I want?
*edit: title is being assigned elsewhere in the code; it is not nil.
That's because if the mutable string mutates, but the property doesn't change (the pointer still points to the same object), then KVC will not consider it a real change. That is probably done for efficiency reasons (in case that didChangeValueForKey: is called but the string is the same, there's no need to update the UI).
I suggest to don't use a mutable string at all, it doesn't fit to your purpose. Just use a normal string, and instead of mutating it reassign it:
-(void)mutateTitle
{
[self willChangeValueForKey:#"title"];
_title= [_title stringByAppendingString: #"mutated"];
[self didChangeValueForKey:#"title"];
}

Crash after dropping pins on map in iOS6

I am trying to learn how to drop pins on a map in iOS 6. I have a code which compiles and runs but which obviously leaks memory -- but when I release (or autoRelease) the mapData object my app crashes. The error is
An instance 0x1b7ac0 of class AddressAnnotation was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger.
There is an earlier post on just this error, also with respect to MapKit:
Setting breakpoint at NSKVODeallocateBreak
But it does not help me:
First, I don't really understand the answer, but also it seems the answer is not relevant to my problem because I am not setting an observer in any way (that I know of, that is!) For example, nowhere in my code do I have the lines
[addressAnnotation addObserver:self forKeyPath:kSelectedAnnotationObserverKeyPath options:NSKeyValueObservingOptionNew context:#"selectedOrDeselected"];
or anything else remotely similar, which were suggested to be the problem.
Having said that, I should also say I do not really understand the concept of the observer -- I have of course created a custom class MapData, which is an NSObject <MKAnnotation> and I suppose that this could also be a source of the problem. But I am basically plumb stupefied.
I have tried to set the suggested symbolic break point but it is not helpful to me: I see I have a BAD ACCESS condition but that is all I really understand!
The code I have written is this:
- (void) showRecordsOnMap
{
NSMutableArray *projectMapAnnotationsArray;
projectMapAnnotationsArray = [[NSMutableArray alloc] init];
int i = 0;
for (i = 0; i < [currentProject.recordArray count]; i++)
{
Record *record = [[[Record alloc] init]autorelease];
record = [currentProject.recordArray objectAtIndex:i];
CLLocationCoordinate2D newCoordinate;
newCoordinate.latitude = record.latitude;
newCoordinate.longitude = record.longitude;
int tag = 0;
NSString *title;
title = [[[NSString alloc] init] autorelease];
title = [NSString stringWithFormat:#"Record %d",record.record_ID];
NSString *subtitle;
subtitle = [[[NSString alloc] init] autorelease];
subtitle = [NSString stringWithFormat:#"Record %d",record.record_ID];
MapData *mapData =[[MapData alloc] initWithCoordinate:newCoordinate withTag:tag withTitle:title withSubtitle:title];
[projectMapAnnotationsArray addObject:mapData];
//[mapData release];
}
[projectMap addAnnotations:projectMapAnnotationsArray];
[projectMapAnnotationsArray release];
}
and then the next needed bit
- (MKAnnotationView *) mapView:(MKMapView *)mapView
viewForAnnotation:(MapData *)annotation
{
static NSString *record = #"record";
//the result of the call is being cast (MKPinAnnotationView *) to the correct
//view class or else the compiler complains
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[projectMap
dequeueReusableAnnotationViewWithIdentifier:record];
if(annotationView == nil)
{
annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:record] autorelease];
}
//if((annotation).tag == 2) annotationView.pinColor = MKPinAnnotationColorRed;
//else annotationView.pinColor = MKPinAnnotationColorGreen;
annotationView.pinColor = MKPinAnnotationColorGreen;
//pin drops when it first appears
annotationView.animatesDrop=TRUE;
//tapping the pin produces a gray box which shows title and subtitle
annotationView.canShowCallout = YES;
return annotationView;
}
This code runs, so long as the mapData object is not released. But clearly I need to release it. As another clue, if I uncomment
// if((annotation).tag == 2) annotationView.pinColor = MKPinAnnotationColorRed;
// else annotationView.pinColor = MKPinAnnotationColorGreen;
I get another error:
[MKUserLocation tag]: unrecognized selector sent to instance 0x9fcb010
2013-05-22 23:05:13.726 Geo360[1175:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MKUserLocation tag]: unrecognized selector sent to instance 0x9fcb010'
but it seems to me that this second error is a simpler stupidity on my part that I at least know how to go about finding. But the error "class AddressAnnotation" has me totally lost. Any help is greatly appreciated!
Edit:
Hi All -- Thank you for taking the time to help. I am still confused. Attached is the code for the MapData object as suggested by AnnaKarenina. Verbumdei suggested I put the array in ViewDidLoad method as a strong property -- I had played with that but I also want to be able to refresh the map pins with an array that may have more data or fewer data, so it seemed to me I needed to make the array anew each time. Perhaps not? AnnaKarenina suggested there may be a release problem in MapData and now that I look at it I am a bit suspicious that I am not releasing tag -- but on the other hand, doing so generates a warning!
Thank you again for helping... still not solved.
MapData.h:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#interface MapData : NSObject <MKAnnotation>
{
NSString *_title;
NSString *subtitle;
NSUInteger tag;
CLLocationCoordinate2D _coordinate;
}
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property(nonatomic) NSUInteger tag;
// Getters and setters
- (id)initWithCoordinate:(CLLocationCoordinate2D)c withTag:(NSUInteger)t withTitle:(NSString *)tl withSubtitle:(NSString *)s;
#end
and MapData.m:
#import "MapData.h"
#implementation MapData
#synthesize coordinate;
#synthesize title;
#synthesize subtitle;
#synthesize tag;
-(id)initWithCoordinate:(CLLocationCoordinate2D)c withTag:(NSUInteger)t withTitle:(NSString *)tl withSubtitle: (NSString *)s
{
if(self = [super init])
{
coordinate = c;
tag = t;
title = tl;
subtitle = s;
}
return self;
}
- (void) dealloc
{
[title release];
[subtitle release];
[super dealloc];
}
#end
Regarding the crash with EXC_BAD_ACCESS, this is most likely due to this code in the MapData initWithCoordinate method:
title = tl;
subtitle = s;
By initializing the instance variables this way, the strings are not retained by the MapData object. When you call [mapData release], the strings are deallocated and then later when the map view tries to access the annotation's title and subtitle, it crashes.
Change the initialization to:
title = [tl copy];
subtitle = [s copy];
and un-comment the [mapData release];
Regarding the "deallocated while key value observers were still registered..." warning message, this is possibly due to invalid coordinates being used for an annotation (see Warning in Custom Map Annotations iPhone). For each annotation, make sure the latitude is from -90 to 90 and the longitude is from -180 to 180.
Regarding the "[MKUserLocation tag]: unrecognized selector" crash, this is unrelated to both of the above issues. This error happens because the viewForAnnotation delegate method is called by the map view for all annotations -- not just the ones you add. This means it is also called for the user location blue dot that the map view itself creates. That user location annotation is of type MKUserLocation while your custom annotation is of type MapData. When the map view calls viewForAnnotation for the user location, that code crashes because the MKUserLocation class does not have a tag property.
The simplest way to handle this is at the top of the viewForAnnotation method, check if the annotation is of type MKUserLocation and return nil for the view (which tells the map view to display the default view which is a blue dot for the user location).
Also, do not change the type declaration of the annotation parameter in the viewForAnnotation method to your custom type (even though it "works"). As explained, the method is called for MKUserLocation as well as your custom class (or classes). Keep it the generic type id<MKAnnotation> which means "an object that implements the MKAnnotation protocol". Then, to access properties of your custom class, cast annotation to your custom class type.
So the top of the viewForAnnotation method should be like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil; //show standard blue dot view for user location
Then change these lines:
if((annotation).tag == 2) annotationView.pinColor = MKPinAnnotationColorRed;
else annotationView.pinColor = MKPinAnnotationColorGreen;
to this:
if ([annotation isKindOfClass:[MapData class]])
{
MapData *mapData = (MapData *)annotation;
if (mapData.tag == 2)
annotationView.pinColor = MKPinAnnotationColorRed;
else
annotationView.pinColor = MKPinAnnotationColorGreen;
}
There are also a few other things (not related to the crash or errors):
This pattern is wrong:
Record *record = [[[Record alloc] init]autorelease];
record = [currentProject.recordArray objectAtIndex:i];
The alloc+init+autorelease is pointless because the variable is then immediately reassigned to another object which has already been allocated. Instead, just declare and assign the local variable:
Record *record = [currentProject.recordArray objectAtIndex:i];
The same applies to how title and subtitle are set in the same method.
In showRecordsOnMap, tag is always 0. Perhaps your code isn't finished yet but be sure to set a different value for each annotation. If you leave the tag as zero for all annotations, the code you put in viewForAnnotation to set the pin color based on tag will not work as expected.
In this line you are passing title for both the title and subtitle parameters:
MapData *mapData =[[MapData alloc] initWithCoordinate:newCoordinate
withTag:tag withTitle:title withSubtitle:title];
In viewForAnnotation, after dequeuing an existing annotation view, you should update the re-used view's annotation property to the current one (add the else part):
if (annotationView == nil)
{
annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:record] autorelease];
}
else
{
//We're re-using a view from another annotation
//that's no longer on screen.
//Update the view's annotation to the current one...
annotationView.annotation = annotation;
}
While the annotations are shown in the map, you must not release the MapData objects. If you want to release the MapData objects, you need to remove the annotations first from the map.
I would suggest you make the projectMapAnnotationsArray as a strong property of the view controller. Allocate it in the viewDidLoad method. Then you can just release it in the dealloc method of the view controller.
Inside the showRecordsOnMap method, you can just add the objects to the projectMapAnnotationsArray without releasing the array itself.

Property not set in drawRect method - iOS

I have been seeing some strange behavior when I try to access a class variable or a property in my drawRect method..
In my .h file I have the following
#interface DartBoard : UIView
{
Board * board;
int index;
}
#property (readwrite, assign, nonatomic) NSNumber * selectedIndex;
#end
In my .m file I have the following
#implementation DartBoard
#synthesize selectedIndex;
-(id)init
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"selectedIndex: %d",[self.selectedIndex intValue]);
NSLog(#"index: %d",index);
}
#end
the output is
2012-06-12 19:48:42.579 App [3690:707] selectedIndex: 0
2012-06-12 19:48:42.580 App [3690:707] index: 0
I have been trying to find a solution but have had no luck..
I found a similar question but there was no real answer to the issue
See: UIView drawRect; class variables out of scope
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly but how do I fix it?
Cheers
Damien
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly
No, there is nothing special about -drawRect:.
There are two possibilities:
1. Your -init method is not being called.
You didn't say how this view gets created -- if you are manually calling [[DartBoard alloc] init], or if it is getting unarchived from a nib file.
If it's coming from a nib, UIView's unarchiving doesn't know that your init method should be called. It will call the designated initializer instead, which is -initWithFrame:.
So, you should implement that method instead, and make sure to call super!
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
}
return self;
}
2. There might be two instances of your view: one that you are manually initing, and another one that comes from somewhere else, probably a nib. The second instance is the one that is being drawn. Since its variables and properties are never set, they show up as zero (the default value).
You could add this line to both your -init and -drawRect: methods, to see what the value of self is. (Or, check it using the debugger.)
NSLog(#"self is %p", self);

NSMutableArray as instance variable alway null

After many hours wasted, I officially turn to the experts for help!
My problem lies with using a NSMutableArray as an instance variable, and trying to both add objects and return the array in a method in my class. I am obviously doing something fundamentally wrong and would be grateful for help...I have already tried all the suggestions from other similar questions on stackoverflow, read apples documentation, and basically all combinations of trial and error coding I can think of. The mutable array just alway returns (null). I've even tried creating properties for them, but still the array returns (null) and then I also am running into memory management problems due to the retain while setting the property, and the init in the init method for the class.
Here is what I am trying to do:
1) Loop through a series of UISwitches and if they are 'switched on', add a string to the NSMutableArray
2) Assign this mutable array to another array in another method
Any help much appreciated,
Andy
And for some code...
fruitsViewController.h
#import <UIKit/UIKit.h>
#interface fruitsViewController : UIViewController
{
NSMutableArray *fruitsArr;
UISwitch *appleSwitch;
UISwitch *orangeSwitch;
}
#property (nonatomic,retain) NSMutableArray *fruitsArr; // ADDED ON EDIT
#property (nonatomic,retain) IBOutlet UISwitch *appleSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *orangeSwitch;
- (IBAction)submitButtonPressed:(id)sender;
#end
fruitsViewController.m
#import "fruitsViewController.h"
#implementation fruitsViewController
#synthesize fruitsArr; // ADDED ON EDIT
#synthesize appleSwitch, orangeSwitch;
/* COMMENTED OUT ON EDIT
-(id)init
{
if (self = [super init]) {
// Allocate memory and initialize the fruits mutable array
fruitsArr = [[NSMutableArray alloc] init];
}
return self;
}
*/
// VIEW DID LOAD ADDED ON EDIT
- (void)viewDidLoad
{
self.fruitsArr = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
self.fruitsArr = nil;
self.appleSwitch = nil;
self.orangeSwitch = nil;
}
- (void)dealloc
{
[fruitsArr release];
[appleSwitch release];
[orangeSwitch release];
[super dealloc];
}
- (IBAction)submitButtonPressed:(id)sender
{
if ([self.appleSwitch isOn]) {
[self.fruitsArr addObject:#"Apple"; // 'self.' ADDED ON EDIT
}
if ([self.orangeSwitch isOn]) {
[self.fruitsArr addObject:#"Orange"; // 'self.' ADDED ON EDIT
}
NSLog(#"%#",self.fruitsArr); // Why is this returning (null) even if the switches are on?!
[fruitsArr addObject:#"Hello World";
NSLog(#"%#",self.fruitsArr); // Even trying to add an object outside the if statement returns (null)
}
#end
It seems like your init function is never called. If you're initializing this view controller from a NIB, you need to use initWithCoder. If not, just declare your fruitsArr in viewDidLoad.
Use view did load instead of init...
- (void)viewDidLoad
{
fruitsArr = [[NSMutableArray alloc] init];
}
Change that init for viewDidLoad and see what happens
Is your init method ever being called (in complicationsViewController). Add a NSLog to check this, you might be calling initWithNib: maybe.
At viewDidUnload you should remove self.fruitsArr = nil;, or, if you want to keep it, then initialize the fruitsArr in viewDidLoad (and remove it from init).
because fruitsArr don't be init.
you should do this first:
fruitsArr = [[NSMutableArray alloc] init];
so, I think you don't run - (id)init before you use fruitsArr.

NSMutableDictionary not retaining values

I am semi-new to Objective-c and confused with why my NSMutableDictionary is not retaining information. I am declaring my variable in the header file:
#interface view_searchResults : UIViewController <UITableViewDataSource, UITableViewDelegate> {
NSMutableDictionary *imageDicationary;
}
#property (nonatomic, retain) NSMutableDictionary *imageDictionary;
Then in my .m file, I have the following:
#synthesize imageDictionary;
-(UIImage *)getImageForURL:(NSURL*)url {
UIImage*image;
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
[imageDictionary setObject:image forKey:#"test"];
if([imageDictionary objectForKey:#"test"]){
NSLog(#"Exists");
}
}
There is obviously other code to support this, but I can confirm that a URL is being passed, and the file is downloading correctly elsewhere. Also, I can confirm that this function is being executed, and I am not referring to the NSMutableDictionary anywhere else in the document.
Thanks!
Where do you create your NSMutable dictionary? If this really is all the code you have you need to create the dictionary:
#implementation view_searchResults
- (id) init;{
self = [super init];
if(self) {
imageDicationary = [NSMutableDictionary alloc] init]; // should also be released in dealloc.
}
return self;
}
If this is the error then the reason you are not causing a crash is because in objective-C it is valid to send a message to the nil object - it just does nothing.
You havent told us whether the "Exists" NSLog is executed, you also are NOT returning the image.
In other words, I fail to see your problem
Has imageDictionary been initialized? (alloc/init?)