Why "copy" is not being invoked? - objective-c

I have the following object:
#interface SomeObject : NSObject
{
NSString *title;
}
#property (copy) NSString *title;
#end
And I have another object:
#interface AnotherObject : NSObject{
NSString *title;
}
#property (copy) NSString *title;
- (AnotherObject*)init;
- (void)dealloc;
- (void) initWithSomeObject: (SomeObject*) pSomeObject;
+ (AnotherObject*) AnotherObjectWithSomeObject (SomeObject*) pSomeObject;
#end
#implementation AnotherObject
#synthesize title
- (AnotherObject*)init {
if (self = [super init]) {
title = nil;
}
return self;
}
- (void)dealloc {
if (title) [title release];
[super dealloc];
}
-(void) initWithSomeObject: (SomeObject*) pSomeObject
{
title = [pSomeObject title]; //Here copy is not being invoked, have to use [ [pSomeObject title] copy]
}
+ (AnotherObject*) AnotherObjectWithSomeObject (SomeObject*) pSomeObject;
{
[pSomeObject retain];
AnotherObject *tempAnotherObject = [ [AnotherObject alloc] init];
[tempAnotherObject initWithSomeObject: pSomeObject];
[pSomeObject release];
return tempAnotherObject;
}
#end
I do not understand, why copy is not being invoked when I am assigning "title = [pSomeObject title]". I always thought if i set "copy" in property it is always going to be invoked. I have a error in my code or I don't understand something?
Thank you in advance.

For a setter to get called you need to use the dot notation.
self.title = [pSomeObject title];
or...to use the dot notation for pSomeObject too
self.title = pSomeObject.title;

Related

Map coordinates not decoding

In my app, I am trying to save the pins that are on the map so that they are there when the user opens the app after it is terminated. I have conformed my mkAnnotation class to NSCoding, and implemented the two required methods. The annotations are all stored in a NSMutableArray in a singleton class, so I am really just trying to save the array in the singleton class. Everything is being encoded fine, but I do not think they are being decoded. Here is some code:
This is my MKAnnotation class:
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#interface MapPoint : NSObject <MKAnnotation, NSCoding>
{
}
- (id)initWithAddress:(NSString*)address
coordinate:(CLLocationCoordinate2D)coordinate
title:(NSString *)t;
#property (nonatomic, readwrite) CLLocationCoordinate2D coordinate;
//This is an optional property from MKAnnotataion
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly, copy) NSString *subtitle;
#property (nonatomic) BOOL animatesDrop;
#property (nonatomic) BOOL canShowCallout;
#property (copy) NSString *address;
#property (nonatomic, copy) NSString *imageKey;
#property (nonatomic, copy) UIImage *image;
#end
#implementation MapPoint
#synthesize title, subtitle, animatesDrop, canShowCallout, imageKey, image;
#synthesize address = _address, coordinate = _coordinate;
-(id)initWithAddress:(NSString *)address
coordinate:(CLLocationCoordinate2D)coordinate
title:(NSString *)t {
self = [super init];
if (self) {
_address = [address copy];
_coordinate = coordinate;
[self setTitle:t];
NSDate *theDate = [NSDate date];
subtitle = [NSDateFormatter localizedStringFromDate:theDate
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_address forKey:#"address"];
NSLog(#"ENCODING coordLatitude %f coordLongitude %f ", _coordinate.latitude, _coordinate.longitude);
[aCoder encodeDouble:_coordinate.longitude forKey:#"coordinate.longitude"];
[aCoder encodeDouble:_coordinate.latitude forKey:#"coordinate.latitude"];
[aCoder encodeObject:title forKey:#"title"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
[self setAddress:[aDecoder decodeObjectForKey:#"address"]];
NSLog(#"DECODING coordLatitude %f coordLongitude %f ", _coordinate.latitude, _coordinate.longitude);
_coordinate.longitude = [aDecoder decodeDoubleForKey:#"coordinate.longitude"];
_coordinate.latitude = [aDecoder decodeDoubleForKey:#"coordinate.latitude"];
[self setTitle:[aDecoder decodeObjectForKey:#"title"]];
}
return self;
}
#end
Here is my singleton class:
#import <Foundation/Foundation.h>
#class MapPoint;
#interface Data : NSObject
{
NSMutableArray *_annotations;
}
#property (retain, nonatomic) NSMutableArray *annotations;
+ (Data *)singleton;
- (NSString *)pinArchivePath;
- (BOOL)saveChanges;
#end
#implementation Data
#synthesize annotations = _annotations;
+ (Data *)singleton {
static dispatch_once_t pred;
static Data *shared = nil;
dispatch_once(&pred, ^{
shared = [[Data alloc] init];
shared.annotations = [[NSMutableArray alloc]init];
});
return shared;
}
- (id)init {
self = [super init];
if (self) {
NSString *path = [self pinArchivePath];
_annotations = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
if (!_annotations) {
_annotations = [[NSMutableArray alloc]init];
}
}
return self;
}
- (NSString *)pinArchivePath {
NSArray *cachesDirectories = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDirectory = [cachesDirectories objectAtIndex:0];
return [cachesDirectory stringByAppendingPathComponent:#"pins.archive"];
}
- (BOOL)saveChanges {
NSString *path = [self pinArchivePath];
return [NSKeyedArchiver archiveRootObject:[Data singleton].annotations
toFile:path];
}
#end
In my viewDidLoad method on the map view controller, I try and place the annotations in the singleton array on the map with this:
for (MapPoint *mp in [Data singleton].annotations) {
[_worldView addAnnotation:mp];
}
The main problem is in the singleton method in these lines:
dispatch_once(&pred, ^{
shared = [[Data alloc] init];
shared.annotations = [[NSMutableArray alloc]init]; //<-- problem line
});
The shared = [[Data alloc] init]; line decodes and initializes the annotations array.
Then the shared.annotations = [[NSMutableArray alloc]init]; line re-creates and re-initializes the annotations array thus discarding the just-decoded annotations so the singleton always returns an empty array.
Remove the shared.annotations = [[NSMutableArray alloc]init]; line.
As already mentioned in the comment, the other minor issue, which causes simply confusion, is the placement of the NSLog where the coordinate is being decoded. The NSLog should be after the decode is done.

Saving the title of a button so it can be accessed in another view (Objective-C)

I'm trying to save the name of a button using a singleton so that the name can be accessed in another view to play a video with the same name. However, I'm getting the error: SIGABRT. I don't really see what's wrong with my code. Any ideas?
#import "List.h"
#import "MyManager.h"
#import "Video.h"
#implementation ExerciseList
-(IBAction) goToVideo:(UIButton *) sender{
MyManager *sharedManager = [MyManager sharedManager];
sharedManager.vidName = [[sender titleLabel] text];
Video *videoGo = [[Video alloc] initWithNibName: #"Video" bundle: nil];
[self.navigationController pushViewController: videoGo animated: YES];
[videoGo release];
}
Here is my .h and .m for MyManager:
#import <foundation/Foundation.h>
#interface MyManager : NSObject {
NSMutableArray *workouts;
NSString *vidName;
}
#property (nonatomic, retain) NSMutableArray *workouts;
#property (nonatomic, retain) NSString *vidName;
+ (id)sharedManager;
#end
#import "MyManager.h"
static MyManager *sharedMyManager = nil;
#implementation MyManager
#synthesize workouts;
#synthesize vidName;
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
- (id)init {
if ((self = [super init])) {
workouts = [[NSMutableArray alloc] init];
vidName = [[NSString alloc] init];
}
return self;
}
-(void) dealloc{
self.workouts = nil;
self.vidName = nil;
[super dealloc];
}
#end
You should access the title of the button
sharedManger.vidName = [sender currentTitle];
However you are not using ARC so also check where your vidName property is retain or copy.
if it is not retain or copy then you can use this code also
if(sharedManger.vidname != nil){
[sharedManger.vidName release];
sharedManger.vidName = nil;
}
sharedManger.vidName = [[sender currentTitle] retain];

NSUserDefaults for self created classes

I'm having a problem in a usually very simple quest, I want to create a custom class containing an integer, a NSString and a NSDate. However, in my example I only use a NSString to show my problem when storing objects.
This is my class header:
#import
#interface MyClass : NSObject <NSCoding> {
NSString * myString;
}
#property (retain) NSString * myString;
#end
and my body:
#import "MyClass.h"
#implementation MyClass
#synthesize myString;
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
myString = [coder decodeObjectForKey:#"myString"];
}
return self;
}
- (id)init {
self = [super init];
if (self) {
myString = #"Hi";
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject: myString forKey:#"myString"];
}
- (void)dealloc {
myString = nil;
[super dealloc];
}
#end
My ViewController contains two buttons called Create and Read, as well as a UILabel called label.
#import <UIKit/UIKit.h>
#interface UserDefaultsTestViewController : UIViewController {
IBOutlet UILabel *label;
}
- (IBAction)pushCreate;
- (IBAction)pushLoad;
#end
I will only show the functions I've edited here:
#import "UserDefaultsTestViewController.h"
#import "MyClass.h"
- (IBAction)pushCreate {
MyClass * myClass = [[MyClass alloc] init];
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:myClass];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:data forKey:#"Test"];
[myClass release];
}
- (IBAction)pushLoad {
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSData * atchive = [defaults objectForKey:#"Test"];
MyClass * myClass = [NSKeyedUnarchiver unarchiveObjectWithData:atchive];
label.text = myClass.myString;
}
However, if I run my code, I get either a EXC_BAD_ACCESS error or it displays it correctly the first time and crashes when doing it a second time, again with the same error.
What am I doing wrong?
You want to do:
self.myString = #"Hi";
Or:
myString = #"Hi";
[myString retain];
Likewise, your dealloc should have:
self.myString = nil;
or
[myString release];

MKAnnotationView's subtitle not getting reloaded

I currently have an XML model getting processed my an NSXMLParser; I get about 340 objects in my model after processing. I place all of the objects on my MKMapView. Once a user "selects" an object (MKAnnotationViewPin); once I start off, the title is populated, coords too (duh?) but the subtitle is set to a place-holder. I process another XML file to retrieve extra information, the subtitle gets updated and populated.
Once the XML file gets parsed I get notified and update its <MKAnnotation> object to reflect the changed subtitle. To reflect the change on the map I have to "unselect" the pin and click it again for it to show the change.
Here is my <MKAnnotation> object:
Header:
#interface CPCustomMapPin : NSObject <MKAnnotation>
{
NSString *_title;
NSString *_annotation;
CLLocationCoordinate2D _coords;
NSString *stationID;
}
#property (nonatomic, retain) NSString *_title;
#property (nonatomic, retain) NSString *_annotation;
#property (nonatomic) CLLocationCoordinate2D _coords;
#property (nonatomic, retain) NSString *stationID;
- (id) initWithTitle: (NSString *) _title withAnnotation: (NSString *) _annotation withCoords: (CLLocationCoordinate2D) _coords withStationID: (NSString *) _id;
Implementation:
#implementation CPCustomMapPin
#synthesize _title, _annotation, _coords, stationID;
- (id) initWithTitle: (NSString *) __title withAnnotation: (NSString *) __annotation withCoords: (CLLocationCoordinate2D) __coords withStationID: (NSString *) _id
{
_title = [[NSString alloc] init];
_annotation = [[NSString alloc] init];
stationID = [[NSString alloc] init];
[self set_title: __title];
[self set_annotation: __annotation];
[self set_coords: __coords];
[self setStationID: _id];
return self;
}
- (NSString *) title
{
return _title;
}
- (NSString *) subtitle
{
return _annotation;
}
- (CLLocationCoordinate2D) coordinate
{
return _coords;
}
- (NSString *) description
{
return [NSString stringWithFormat: #"title: %# subtitle: %# id: %#", _title, _annotation, stationID];
}
- (void) dealloc
{
[_title release];
[_annotation release];
[stationID release];
[super dealloc];
}
#end
Thank you for your valuable input.
Apparently no one knows… I just found this technique:
- (void) closeAnnotation: (id <MKAnnotation>) annotation inMapView: (MKMapView *) mapView
{
[mapView deselectAnnotation: annotation animated: NO];
[mapView selectAnnotation: annotation animated: YES];
}
And of course you call your method accordingly:
Example:
- (void) myMethod
{
for (id <MKAnnotation> _annotation in mapView.annotations)
{
[self closeAnnotation: _annotation inMapView: mapView];
}
}

incompatible Objective-c types warning

I have a user class that I use through the iPhone application, this is the init and initWithUser functions from my user class (A SUBCLASS OF NSobject), when I use the initWithUser function I get the warning described after the code. please advise.
// serialize.h
#import <Foundation/Foundation.h>
#protocol Serialize
// serialize the object to an xml string
-(NSString*)ToXML;
#end
// user.h
#import <Foundation/Foundation.h>
#import "Serialize.h"
#import "Contact.h"
#interface User : NSObject <Serialize> {
NSString *email;
NSString *firstName;
NSString *lastName;
NSString *userId;
NSString *userName;
NSString *password;
NSMutableArray *contactList;
}
#property (nonatomic,copy) NSString *email;
#property (nonatomic,copy) NSString *firstName;
#property (nonatomic,copy) NSString *lastName;
#property (nonatomic,copy) NSString *userId;
#property (nonatomic,copy) NSString *userName;
#property (nonatomic,copy) NSString *password;
#property (nonatomic, retain) NSMutableArray *contactList;
//-(id)init;
-(id)initWithUser:(User *)copyUser;
#end
// user.m
#import "user.h"
#implementation User
#synthesize email;
#synthesize firstName;
#synthesize lastName;
#synthesize userId;
#synthesize userName;
#synthesize password;
#synthesize contactList;
-(id)init
{
// call init in parent and assign to self
if( (self = [super init]) )
{
// do something specific
contactList = [[NSMutableArray alloc] init];
}
return self;
}
-(id)initWithUser:(User *)copyUser
{
if( (self = [self init]) ) {
email = copyUser.email;
firstName = copyUser.firstName;
lastName = copyUser.lastName;
userId = copyUser.userId;
userName = copyUser.userName;
password = copyUser.password;
// release contactList initialized in the init
[contactList release];
contactList = [copyUser.contactList mutableCopy];
}
return self;
}
- (void)dealloc
{
// TODO:
[contactList removeAllObjects];
[contactList release];
[super dealloc];
}
// implementation of serialize protocol
-(NSString*)ToXML
{
return #"";
}
and I use it in the main controller this way
- (void) registerNewUser {
RegistrationViewController *regController = [[RegistrationViewController alloc] init] ;
regController.newUser = [[User alloc] initWithUser:self.user];
[self.navigationController pushViewController:regController animated:YES];
[regController release];
}
the line
regController.newUser = [[User alloc] initWithUser:self.user];
gives me the following error, and its been driving me nuts for a couple of days:
incompatible Objective-c types 'struct User*', expected 'struct NSString *' when passing argument 1 of 'initWithUser:' from distinct Objective-c type
any help and guidance is appreciated
The problem is you have an ambiguous selector. Because alloc returns id, the call to initWithUser: has become ambiguous. NSUserDefaults also has an initWithUser: function which takes a string. The compiler thinks you're trying to use that one. Change the line to
regController.newUser = [(User*)[User alloc] initWithUser:self.user];
and everything should work as expected.
As mentioned in the comments, there are other problems with your implementation. In your initializer, reusing the -init is redundant and the assignments to ivars like email should be taking ownership of the data using -copy or -retain:
-(id)initWithUser:(User *)copyUser {
if((self = [super init])) {
// take ownership of the users data by copying or retaining:
email = [copyUser.email copy];
// ...
contactList = [copyUser.contactList mutableCopy];
}
return self;
}
In -dealloc, -removeAllObjects can be removed and the member data has to be released:
- (void)dealloc {
[email release];
// ...
[contactList release];
[super dealloc];
}
Note that you are also leaking the new User instance if newUser is a copy or retain property as there is a release missing:
User *user = [[User alloc] initWithUser:self.user];
regController.newUser = user;
[user release];