Obj-C: Clone object with a mutated property - objective-c

I would like to a create convenience initiator by cloning an existing object with a mutation, while keeping the original object intact.
For example:
Given a Person object person1 with a name (Tom) and age (10)
I would like to clone the person1 object, but with 0 age.
I have following code in Obj-C, but not sure if there's a better way to do it:
Person.h
#interface Person : NSObject
#property (nonatomic, readonly) NSString *name;
#property (nonatomic, readonly) NSUInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
- (instancetype)cloneWithZeroAge;
#end
Person.m
#implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age
{
if (self = [super init]) {
_name = name;
_age = age;
}
return self;
}
- (instancetype)cloneWithZeroAge
{
if (self) {
// mutate age to 0
return [self initWithName:_name age:0];
}
return self;
}
#end

Let's start with the constructor. For you specific scenario it doesn't make much difference, but in order to be functionally independent, you better ensure that the name gets a copy of the data passed. You also want to specify this part as memory storage modifier of the property, so the contract is apparent to the client code:
#interface Person : NSObject
#property (copy, nonatomic, readonly) NSString *name;
...
#end
#implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age
{
if (self = [super init]) {
_name = [name copy];
_age = age;
}
return self;
}
Now for the actual "copy" method. In order to be more consistent with existing NSCopying protocol and Objective-C naming convention, and, which is more important, the memory management ownership convention, you should start the method name with the word "copy", so the calling side knows it's responsible for releasing the object. The most important part now, is that if you want to keep the original object untouched, you have to allocate and create a new object. In your implementation, however, you just change the self into the new object entirely. Here is how I would implement such a method:
- (instancetype)copyWithZeroAge {
Person *copy = [[Person alloc] initWithName:_name age:0];
return copy;
}
If you prefer to keep the name property memory modifier strong instead of copy, don't forget to copy the instance:
- (instancetype)copyWithZeroAge {
Person *copy = [[Person alloc] initWithName:[_name copy]
age:0];
return copy;
}

Related

Why method is not getting called

I'm trying to learn how to make a class and object and how to call methods in Objective-C. My small program creates an object of the City class, allows to name that object, set an age, population, and get these values to print. But when I call a method to set these values, I get a (null) and zeros in result. Here's my code:
City.h
#import <Foundation/Foundation.h>
#interface City : NSObject
-(void) setName:(NSString *)theName Age:(int)theAge Population:(int)thePopulation;
-(void) getName;
-(void) getAge;
-(void) getPopulation;
-(void) nextDay;
#end
City.m
#import "City.h"
#implementation City
{
NSString *name;
int age;
int population;
}
-(void) setName:(NSString *)theName Age:(int)theAge Population:(int)thePopulation
{
theName = name;
theAge = age;
thePopulation = population;
}
-(void) getName
{
NSLog(#"Name is %#", name);
}
-(void) getAge
{
NSLog(#"Age is %d", age);
}
-(void) getPopulation
{
NSLog(#"Population today is %d", population);
}
main.m
int main()
{
City *moscow = [[City alloc] init];
[moscow setName:#"Msk" Age:120 Population:1000];
[moscow getName];
[moscow getAge];
[moscow getPopulation];
}
the result of the running is:
Name is (null)
Age is 0
Population today is 0
Program ended with exit code: 0
what am I doing wrong?
The problem is that the instance variables of City is never set. The code in setName:Age:Population: assigns the values of the instance variables (name, age, and population) to the arguments variables (theName, theAge, and thePopulation). Swapping these will cause the setter to assign the arguments to the instance variables:
name = theName;
age = theAge;
population = thePopulation;
That said, it's more idiomatic Objective-C to use properties—instead of instance variables and manual getters and setters—and to use an initializer to set the initial values. With those changes the City class would look something like this:
City.h
NS_ASSUME_NONNULL_BEGIN
#interface City : NSObject
#property (copy) NSString *name;
#property (assign) NSInteger age;
#property (assign) NSInteger population;
- (instancetype)initWithName:(NSString *)name
age:(NSInteger)age
population:(NSInteger)population;
#end
NS_ASSUME_NONNULL_END
City.m
#import "City.h"
#implementation City
- (instancetype)initWithName:(NSString *)name
age:(NSInteger)age
population:(NSInteger)population
{
self = [super init];
if (self) {
_name = [name copy];
_age = age;
_population = population;
}
return self;
}
#end
Two things to note about this code:
The string is copied—both in the initializer and in the property—to protect against having a NSMutableString is passed and later be mutated (which would mutate the value of name as well. For the common case where an immutable NSString is passed, the copy is equivalent to a "retain".
The synthesized instance variables are used when assigning values in the initializer. This is to protect against having a subclass override any of these properties and have a custom setter method run before the object is fully initialized (have all its variables set to their initial values). This only applies to initializers, custom setters, and dealloc. Everything else should use the properties to access and modify these values.

Objective-c readonly copy properties and ivars

I'm try to grok properties declared as both copy and readonly in objective-c, and specifically, whether I have to do the copy myself. In my init methods. Evidence suggests I do:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData *test;
- (instancetype)initWithData:(NSData *)data;
#end
#implementation A
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
_test = data;
}
return self;
}
#end
int main (void) {
NSData *d1 = [NSMutableData dataWithBytes:"1234" length:5];
A *a = [[A alloc] initWithData:d1];
NSLog(#"%lx", (unsigned long)d1);
NSLog(#"%lx", (unsigned long)a.test);
return 0;
}
I had thought I could do self.test = data in my init method, but that is not permitted because it's readonly (not unexpectedly). Of course, self.test = [data copy] ensures two different objects.
So: Is there a way to create a readonly property in objective-c that copies the incoming value, or is it sufficiently an edge case that the combination is pointless and I have to do any copying myself manually anyway?
A #property declaration is merely shorthand for some accessor/mutator method declarations, and (in some cases) synthesized implementations for said accessor/mutator methods.
In your case, the #property(nonatomic, copy, readonly) NSData *test declaration expands to this equivalent code:
#interface A : NSObject
{
NSData* _test;
}
- (NSData*)test;
#end
#implementation A
- (NSData*)test
{
return _test;
}
#end
There is no setTest: mutator method because the property is declared as readonly, so the copy attribute has no effect.
You can implement your own mutator method:
- (void)setTest:(NSData*)newValue
{
_test = [newValue copy];
}
Or, you can have the compiler synthesize a mutator method for you by declaring a read/write property in a private class extension in your implementation file:
// A.m:
#interface A()
#property (nonatomic, copy) NSData* test;
#end
Both cases would allow you to use the test mutator method to copy a value to the _test instance variable:
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
The end result is:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData* test;
- (instancetype)initWithData:(NSData*)data;
#end
#interface A()
#property (nonatomic, copy) NSData* test;
#end
#implementation A
- (instancetype)initWithData:(NSData*)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
#end
In addition to what Darren said, the copy attribute describes what semantics the properties setter has. In your initializer, you're not using the setter, you're directly assigning to the instance variable.
It's maybe a bit hard to grok, but the instance variable is not the same thing as the property. It is used to implement the property in this case. But, assigning to the instance variable is not the same as setting the property.
If you want your initializer to also have the semantics that it copies the passed-in data, that's a separate design decision (although a good idea to go with the property's semantics). You could implement that by using a private setter as Darren suggests, but you could also just do:
_test = [data copy];
in the initializer.

Is the object retained when setting a property's ivar directly in -init?

My understanding is that instance variables should be accessed directly from inside the init method. For example:
#interface ABC : NSObject
#property (strong, nonatomic) NSString *name;
#end
#implementation ABC
- (id)init
{
if ((self = [super init]) != nil)
{
_name = #"some name";
}
}
// another init example
- (id)initWithName:(NSString*)n
{
if ((self = [super init]) != nil)
{
_name = n;
}
}
#end
I am wondering about the _name variable. In both init examples, is _name retained? For this example, I am using ARC.
Whether _name is retained in this code depends on whether you have ARC turned on. If you do, ARC will retain the object for you (since that is ARC's job). If you don't have ARC turned on, you need to retain it yourself, which would look like:
- (id)initWithName:(NSString*)n
{
if ((self = [super init]) != nil)
{
_name = [n retain];
}
}
(It's also worth pointing out that NSStrings should usually be copied rather than retained, so you would make the property #property (copy, nonatomic) NSString *name; and the assignment would be _name = [n copy].)

Is that the correct way to deal with ivars?

I have read a lot of topics about getters and setters. I know what they are and why are useful. Different source claim's different ways to release the ivars. Here begins my confusion
#interface CoolClass : NSObject
{
NSString *name;
}
#property (nonatomic, copy) NSString *name;
#end
#implementation CoolClass
#synthesize name = _name;
-(id)init
{
if(super = [self super])
{
self.name = #"Jo";
}
return self;
}
-(void)dealloc
{
[self.name release], self.name = nil;
}
#end
Is that the correct way to release/free ivars ?
You'll want to use accessors most of the time, but not in partially constructed states because they can have negative side-effects. Here's how it's done:
- (id)init
{
if((self = [super init])) {
// self.name = #"Jo"; << don't use accessors in initializer
_name = [#"Jo" copy]; << good
}
return self;
}
// added for another variation:
- (id)initWithName:(NSString *)inName
{
if((self = [super init])) {
_name = [inName copy];
}
return self;
}
- (void)dealloc
{
// don't use accessors in dealloc
// don't release the result of a getter (release the result of the retained or copied result)
// [self.name release], self.name = nil;
// instead:
[_name release], _name = nil;
[super dealloc]; << compiler should have warned you about this one
}
Note: In the case of init, the string literal is an immortal and it won't matter if you copy it because the copy just returns itself. My preference is to just 'copy' the immortal for clarity, although it's unnecessary.
Here is what I would advise:
#interface CoolClass : NSObject
#property (nonatomic, copy) NSString *name;
#end
#implementation CoolClass
#synthesize name = _name;
-(id)init
{
if(super = [self super])
{
self.name = #"Jo";
}
return self;
}
-(void)dealloc
{
[_name release];
[super dealloc];
}
#end
Notes:
There is no need to explicitly declare ivars inside { ... } in your header. They will be created automatically when you synthesise your property. Explicit ivars are a legacy concept that are no longer needed since about iOS 3.
You should not use self.name in the dealloc as this calls the getter method, which may do additional work beyond merely fetching the ivar. Normally it's good practice to use the getter method, but in the dealloc you should release the ivar directly
It is good practice to set ivars to nil after releasing them, but again in the dealloc this in not necessary because no code is ever executed after dealloc, so the pointer won't be referenced again.
Normally (outside of the dealloc), if you wish to release an ivar you should set it to nil using the setter like this: self.name = nil; that will automatically release it and set it to nil. This is equivalent to [_name release], _name = nil;
#interface CoolClass : NSObject
{
NSString *name;
}
You declared here an instance variable 'name'; Nowadays there is no need to declare ivars in the header file. Just use properties and make the compiler to synthesize ivar for you.
#property (nonatomic, copy) NSString *name;
Here we have a property declaration that specifies that a copy of the object should be used for assignment and that a previous value is sent a release message.
In implementation you want to synthesize your property:
#synthesize name = _name;
This code tells the compiler to generate a getter and setter for property called 'name' and use instance variable called '_name' to store value. So you have now two ivars - 'name' and '_name'.
That how init method should like like:
-(id)init
{
if(self = [super init])
{
name = #"This is ivar declared between {}";
_name = #"synthesized ivar";
}
return self;
}
And the dealloc:
-(void)dealloc
{
[name release];
[_name release];
[super dealloc];
}

Why does this property need the 'retain'?

Given the following definition of a class with retain properties:
#interface FeedEntry : NSObject<NSCoding>
{
NSURL* url;
NSData* source;
}
#property (retain) NSURL* url;
#property (retain) NSData* source;
#end
#implementation FeedEntry
#synthesize url;
#synthesize source;
-(void)encodeWithCoder:(NSCoder*)coder
{
[coder encodeObject:url forKey:#"url"];
[coder encodeObject:source forKey:#"source"];
}
Why does the url property in initWithCoder method need the "retain":
-(id)initWithCoder:(NSCoder*)coder
{
url = [[coder decodeObjectForKey:#"url"] retain];
source = [coder decodeObjectForKey:#"source"];
NSLog(#"got url=%#\n", url);
return self;
}
Specifically, why doesn't the synthesized "get url" method retain the object? (I'm guessing the source property will need a retain as well).
Quick answer:
When you set:
url = [[coder decodeObjectForKey:#"url"] retain];
you are not using the #property. You are manually setting the value of the instance variable url. You must, therefore, also manually retain the value.
To set the variable using the synthesized properties, you would instead call:
[self setUrl:[coder decodeObjectForKey:#"url"]];
or
self.url = [coder decodeObjectForKey:#"url"];
Either of these forms would make use of the synthesized methods, and handle the retain automatically.
Details:
In Objective-C, the #property and #synthesize keywords automatically create the getter and setter methods for you:
#interface MyClass
{
id someValue;
}
#property (retain) id someValue;
#end
#implementation MyClass
#synthesize someValue;
#end
Is equivalent to:
#interface MyClass
{
id someValue;
}
- (id)someValue;
- (void)setSomeValue:(id)newValue;
#end
#implementation MyClass
- (id)someValue { return someValue; }
- (void)setSomeValue:(id)newValue
{
[newValue retain];
[someValue release];
someValue = newValue;
}
#end
This creates an important distinction between the "internal" member variable and the property having the same name. If you reference the member variable by name, you are bypassing the synthesized property methods.