I have this simple class which I know is ok in terms of memory leaks.
#interface location : NSObject {
NSString *name;
float lat;
float lon;
NSString *subtitle;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *subtitle;
#property (nonatomic, assign) float lat;
#property (nonatomic, assign) float lon;
#end
#implementation location
#synthesize name;
#synthesize lon;
#synthesize lat;
#synthesize subtitle;
-(void)dealloc{
[name release];
[subtitle release];
[super dealloc];
}
#end
There is retain in the #property so i release in the dealloc method. Now, my question is: If I alloc one of the strings in a init method or some other method I create, should I do another release? If so, when?
#implementation location
#synthesize name;
#synthesize lon;
#synthesize lat;
#synthesize subtitle;
-(void) init{
name = [[NSString alloc] init];
}
-(void)dealloc{
[name release]; // IS THIS CORRECT?!
[subtitle release];
[super dealloc];
}
#end
If you are assigning value using self. notation then you should release (as retain was called automatically, if you use synthesize) if your were using alloc + init approach for creating new object. Example:
NSString *str = [[NSString alloc] init];
self.name = str;
[str release];
If you are assigning value using self. notation and assigning autoreleased object then you shouldn't retain and release. Example:
NSString *str = [NSString string];
self.name = str;
If you are assigning value without self. prefix then you should not assign autorelease object and should not release, you just should alloc + init object. Example:
NSString *str = [[NSString alloc] init];
name = str;
Or if you want to assign autoreleased object without self. prefix then you should retain it. Example:
NSString *str = [NSString string];
name = [str retain];
In dealloc method you should release objects if you didn't do that earlier.
Basically you don't need retain here as you are not reatining anything. Your code is same as if all the properties are assigned.
until you don't call self.name, the retain semantics is not applied.
There is a code style which remove that confusion. keep the property name and instance name different and assign instance name to property name at #synthesize.
Like this:
#interface location : NSObject {
NSString *name;
}
#property (nonatomic, retain) NSString *nameProp;
#end
#implementation location
#synthesize nameProp = name;
Related
Put simply, is there a way to receive a general notification when any property in an Objective-C class is changed? I know I can use KVO to monitor particular property changes, but I have the need to call a particular method whenever any setProperty: message is sent to my class. I want to be able to receive a generic notification without any concern about which property in particular was modified.
If it helps to clarify why I want to do this, I am making use of some fast table scrolling code found here: http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/
Part of the process of accomplishing this is that whenever a property in a table view cell is modified, [ self setNeedsDisplay ] needs to be called. I'd rather not have to override the setter methods for every property in my class just to make this call.
As Chuck notes, you can create a dependent key, or of course you can directly observe all the properties (which is less work than overloading the setters).
Using the Objective-C runtime, if you exclusively use properties, you can automate this process using class_copyPropertyList(). But I'd probably only do this if this problem comes up a bit for you. If you only have one instance of this problem, it's probably easier and safer and more maintainable just to directly observe the list of properties unless you feel like working in the ObjC runtime.
Here's an example built off of Chuck and Rob's suggestions:
DrakeObject.h
#interface DrakeObject : NSObject
#property (nonatomic, strong) NSNumber *age;
#property (nonatomic, strong) NSNumber *money;
#property (nonatomic, strong) NSString *startPosition;
#property (nonatomic, strong) NSString *currentPosition;
#property (nonatomic, strong, readonly) id propertiesChanged;
#end
DrakeObject.m
#implementation DrakeObject
- (instancetype)init {
self = [super init];
if (self) {
self.age = #25;
self.money = #25000000;
self.startPosition = #"bottom";
self.currentPosition = #"here";
}
return self;
}
- (id)propertiesChanged {
return nil;
}
+(NSSet *)keyPathsForValuesAffectingPropertiesChanged {
return [NSSet setWithObjects:#"age", #"money", #"startPosition", #"currentPosition", nil];
}
observing propertiesChanged will let us know anytime a property has changed.
[self.drakeObject addObserver:self
forKeyPath:#"propertiesChanged"
options:NSKeyValueObservingOptionNew
context:nil];
Not exactly. You can create a dependent key that depends on every property you wish to expose and then observe that. That's about as close as you'll get, I think.
Here an example of code. I have a general object and dother object. Dother object has to save his state on change each property.
#import <Foundation/Foundation.h>
#interface GeneralObject : NSObject
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary;
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary;
- (NSDictionary *)dictionaryValue;
- (NSArray *)allPropertyNames;
#end
implementation
#import "GeneralObject.h"
#import <objc/runtime.h>
#implementation GeneralObject
#pragma mark - Public
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary {
return [[self alloc] initWithDictionary:aDictionary];
}
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary {
aDictionary = [aDictionary clean];
for (NSString* propName in [self allPropertyNames]) {
[self setValue:aDictionary[propName] forKey:propName];
}
return self;
}
- (NSDictionary *)dictionaryValue {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSArray *propertyNames = [self allPropertyNames];
id object;
for (NSString *key in propertyNames) {
object = [self valueForKey:key];
if (object) {
[result setObject:object forKey:key];
}
}
return result;
}
- (NSArray *)allPropertyNames {
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *array = [NSMutableArray array];
unsigned i;
for (i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[array addObject:name];
}
free(properties);
return array;
}
#end
and after all we have dother class, which should save his state on each change of any property
#import "GeneralObject.h"
extern NSString *const kUserDefaultsUserKey;
#interface DotherObject : GeneralObject
#property (strong, nonatomic) NSString *firstName;
#property (strong, nonatomic) NSString *lastName;
#property (strong, nonatomic) NSString *email;
#end
and implementation
#import "DotherObject.h"
NSString *const kUserDefaultsUserKey = #"CurrentUserKey";
#implementation DotherObject
- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
if (self = [super initWithDictionary:dictionary]) {
for (NSString *key in [self allPropertyNames]) {
[self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil];
}
}
return self;
}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context {
NSDictionary *dict = [self dictionaryValue];
[[NSUserDefaults standardUserDefaults] setObject:dict forKey:kUserDefaultsUserKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)description {
return [NSString stringWithFormat:#"%#; dict:\n%#", [super description], [self dictionaryValue]];
}
#end
Happy coding!
I am trying to use singleton in my app. I want to share 8 strings using a singleton.
This is the tutorial I referred to -> http://www.galloway.me.uk/tutorials/singleton-classes/
My code:
- MyManager.h
#import <foundation/Foundation.h>
#interface MyManager : NSObject {
NSString *someProperty1;
NSString *someProperty2;
NSString *someProperty3;
NSString *someProperty4;
NSString *someProperty5;
NSString *someProperty6;
NSString *someProperty7;
NSString *someProperty8;
}
#property (nonatomic, retain) NSString *someProperty1;
#property (nonatomic, retain) NSString *someProperty2;
#property (nonatomic, retain) NSString *someProperty3;
#property (nonatomic, retain) NSString *someProperty4;
#property (nonatomic, retain) NSString *someProperty5;
#property (nonatomic, retain) NSString *someProperty6;
#property (nonatomic, retain) NSString *someProperty7;
#property (nonatomic, retain) NSString *someProperty8;
+ (id)sharedManager;
#end
MyManager.m
#import "MyManager.h"
#implementation MyManager
#synthesize someProperty;
#pragma mark Singleton Methods
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
someProperty1 = [[NSString alloc] initWithString:#"Default Property Value"];
someProperty2 = [[NSString alloc] initWithString:#"Default Property Value"];
someProperty3 = [[NSString alloc] initWithString:#"Default Property Value"];
someProperty4 = [[NSString alloc] initWithString:#"Default Property Value"];
someProperty5 = [[NSString alloc] initWithString:#"Default Property Value"];
someProperty6 = [[NSString alloc] initWithString:#"Default Property Value"];
someProperty7 = [[NSString alloc] initWithString:#"Default Property Value"];
someProperty8 = [[NSString alloc] initWithString:#"Default Property Value"];
}
return self;
}
- (void)dealloc {
// Should never be called, but just here for clarity really.
}
#end
I want to use this singleton to use these string variables to add string in one other view and then use it to store in database in another third view.
Can someone please tell me how to reference them and store string in these variables and again access them in the third different view?
import MyManager.h where you use singleton.
[[MyManager sharedManager] setSomeProperty1:#abc"]; //for setting
[[MyManager sharedManager] someProperty1]; // for getting
or you can use like this also
MyManager *manager = [MyManager sharedManager];
manager.someProperty1 = #"abc";
NSString *str = manager.someProperty1;
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];
this is my data strucure:
group [1...n] {
id,
name,
elements : [1...n]
}
I define a class for element with all properties and a class for group as:
#interface Group : NSObject {
NSInteger groupID;
NSString *groupName;
NSMutableArray *elements;
}
#property (assign, readwrite) NSInteger groupID;
#property (assign, readwrite) NSString *groupName;
#property (assign, readwrite) NSMutableArray *elements;
and single element with:
#interface Element : NSObject {
NSInteger elementID;
NSString *elementName;
}
#property (assign, readwrite) NSInteger elementID;
#property (assign, readwrite) NSString *elementName;
Both classes have properties and synthesize.
When application start I inserted data on data structure with this:
arrGroup = [NSMutableArray array];
[arrGroup retain];
Element *element1 = [[Element alloc] init];
element1.elemenID = 1;
element1.elemenName = #"Andrea";
Element *element = [[Element alloc] init];
element2.elementID = 2;
element2.elementName = #"Andrea2";
Group *group = [[Group alloc] init];
group.groupID = 1;
group.groupName = #"Grup 1";
[group.elements addObject:element1];
[group.elements addObject:element2];
[contact1 release];
[contact2 release];
[arrGroup addObject:group];
The problem is this the [group.elements addObjct:element1]. Nothing has been written on elements NSMutableArray.
Could you help me to find the error? There is a better method to retrieve structure data (groups of elemens)?
thanks for help!
Andrea
#synthesize only generates the getter and the setter for your property, you have to take care of initialization yourself if needed.
To initialize the mutable array do e.g. this in your initializer:
- (id)init { // or however it is named
if ((self = [super init])) {
elements = [[NSMutableArray alloc] init];
// ... more?
}
return self;
}
- (void)dealloc {
[elements release]; // don't forget to clean up
// ... more?
[super dealloc];
}
Put simply, is there a way to receive a general notification when any property in an Objective-C class is changed? I know I can use KVO to monitor particular property changes, but I have the need to call a particular method whenever any setProperty: message is sent to my class. I want to be able to receive a generic notification without any concern about which property in particular was modified.
If it helps to clarify why I want to do this, I am making use of some fast table scrolling code found here: http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/
Part of the process of accomplishing this is that whenever a property in a table view cell is modified, [ self setNeedsDisplay ] needs to be called. I'd rather not have to override the setter methods for every property in my class just to make this call.
As Chuck notes, you can create a dependent key, or of course you can directly observe all the properties (which is less work than overloading the setters).
Using the Objective-C runtime, if you exclusively use properties, you can automate this process using class_copyPropertyList(). But I'd probably only do this if this problem comes up a bit for you. If you only have one instance of this problem, it's probably easier and safer and more maintainable just to directly observe the list of properties unless you feel like working in the ObjC runtime.
Here's an example built off of Chuck and Rob's suggestions:
DrakeObject.h
#interface DrakeObject : NSObject
#property (nonatomic, strong) NSNumber *age;
#property (nonatomic, strong) NSNumber *money;
#property (nonatomic, strong) NSString *startPosition;
#property (nonatomic, strong) NSString *currentPosition;
#property (nonatomic, strong, readonly) id propertiesChanged;
#end
DrakeObject.m
#implementation DrakeObject
- (instancetype)init {
self = [super init];
if (self) {
self.age = #25;
self.money = #25000000;
self.startPosition = #"bottom";
self.currentPosition = #"here";
}
return self;
}
- (id)propertiesChanged {
return nil;
}
+(NSSet *)keyPathsForValuesAffectingPropertiesChanged {
return [NSSet setWithObjects:#"age", #"money", #"startPosition", #"currentPosition", nil];
}
observing propertiesChanged will let us know anytime a property has changed.
[self.drakeObject addObserver:self
forKeyPath:#"propertiesChanged"
options:NSKeyValueObservingOptionNew
context:nil];
Not exactly. You can create a dependent key that depends on every property you wish to expose and then observe that. That's about as close as you'll get, I think.
Here an example of code. I have a general object and dother object. Dother object has to save his state on change each property.
#import <Foundation/Foundation.h>
#interface GeneralObject : NSObject
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary;
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary;
- (NSDictionary *)dictionaryValue;
- (NSArray *)allPropertyNames;
#end
implementation
#import "GeneralObject.h"
#import <objc/runtime.h>
#implementation GeneralObject
#pragma mark - Public
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary {
return [[self alloc] initWithDictionary:aDictionary];
}
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary {
aDictionary = [aDictionary clean];
for (NSString* propName in [self allPropertyNames]) {
[self setValue:aDictionary[propName] forKey:propName];
}
return self;
}
- (NSDictionary *)dictionaryValue {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSArray *propertyNames = [self allPropertyNames];
id object;
for (NSString *key in propertyNames) {
object = [self valueForKey:key];
if (object) {
[result setObject:object forKey:key];
}
}
return result;
}
- (NSArray *)allPropertyNames {
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *array = [NSMutableArray array];
unsigned i;
for (i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[array addObject:name];
}
free(properties);
return array;
}
#end
and after all we have dother class, which should save his state on each change of any property
#import "GeneralObject.h"
extern NSString *const kUserDefaultsUserKey;
#interface DotherObject : GeneralObject
#property (strong, nonatomic) NSString *firstName;
#property (strong, nonatomic) NSString *lastName;
#property (strong, nonatomic) NSString *email;
#end
and implementation
#import "DotherObject.h"
NSString *const kUserDefaultsUserKey = #"CurrentUserKey";
#implementation DotherObject
- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
if (self = [super initWithDictionary:dictionary]) {
for (NSString *key in [self allPropertyNames]) {
[self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil];
}
}
return self;
}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context {
NSDictionary *dict = [self dictionaryValue];
[[NSUserDefaults standardUserDefaults] setObject:dict forKey:kUserDefaultsUserKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)description {
return [NSString stringWithFormat:#"%#; dict:\n%#", [super description], [self dictionaryValue]];
}
#end
Happy coding!