NSCopying & Inheritance - objective-c

Please have a look on following code :------- .h
#interface BankAccount : NSObject<NSCopying>
{
double accountBalance;
long accountNumber;
NSString *CustomerName;
NSString *AccountType;
}
-(void) setAccount: (long) y andBalance: (double) x;
-(void) setCustomerName: (NSString*) name andAccountType: (NSString*) type;
-(id)copyWithZone:(NSZone *)zone;
#end
#interface Savings : BankAccount
{
int number;
NSString *Offer;
}
-(void) setSavingNumber: (uint8_t) num andOffer: (NSString*) offer;
-(id)copyWithZone:(NSZone *)zone;
#end
---------- .m
#implementation BankAccount
-(void) setAccount: (long) y andBalance: (double) x
{
accountNumber = y;
accountBalance = x;
}
-(void) setCustomerName: (NSString*) name andAccountType: (NSString*) type
{
CustomerName = name;
AccountType = type;
}
-(id)copyWithZone:(NSZone *)zone
{
BankAccount *accountCopy = [[BankAccount allocWithZone: zone] init];
[accountCopy setAccount: accountNumber andBalance: accountBalance];
[accountCopy setCustomerName:CustomerName andAccountType:AccountType];
return accountCopy;
}
#end
#implementation Savings
-(void) setSavingNumber: (uint8_t) num andOffer: (NSString*) offer
{
number = num;
Offer = offer;
}
-(id)copyWithZone:(NSZone *)zone
{
Savings * clone = [super copyWithZone:zone];
[clone setSavingNumber:number andOffer:Offer];************** error *********
return clone;
}
#end
When run this code::::::
Savings* account1;
Savings* account2;
account1 = [[Savings alloc] init];
[account1 setAccount:10 andBalance:1000.10];
[account1 setCustomerName:[NSString stringWithFormat:#"%#",#"Deepak"] andAccountType:[NSString stringWithFormat:#"%#",#"Savings"]];
[account1 setSavingNumber:2001 andOffer:#"Bad"];
account2 = [account1 copy];
#
i dont know what is wrong with the code please help me. thanks in advance.
Thanks
Deepak

Your base copyWithZone: method should look like this:
-(id)copyWithZone:(NSZone *)zone {
// change this line to use [self class]
BaseClass *base = [[[self class] allocWithZone:zone] init];
// copy base members:
// ...
return base;
}
Your derived copyWithZone: methods should look like this:
-(id)copyWithZone:(NSZone *)zone {
DerivedClass *derived = [super copyWithZone:zone];
// copy derived members:
// ...
return derived;
}
Also make sure sure you are making deep copies of strong references and shallow copies of weak references. Thus to for example copy members of type NSString and NSArray (each one with strongly referenced members and one with weak) do:
derived.strongString = [[strongString copyWithZone:zone] autorelease];
derived.weakString = weakString;
derived.strArrWStrVals = [[strArrWStrVals copyWithZone:zone] autorelease];
derived.strArrWWeakVals = [[[NSArray allocWithZone:zone]
initWithArray:strArrWWeakVals] autorelease];
derived.weakArray = weakArray;
(Usually weak members are also assigned/retained and strong variables are copied.)
Take note that because of this you should not use initWithMyself: type methods to copy data.

Ok first, your test code is bad because you set a saving number of 2001 in an 8 bit integer. Second, your code does not run because you're trying to call setSavingNumber:andOffer: on a BankAccount instead of an Saving object so it can't find the selector for this method at run time. Thanks David for pointing that out.
When implementing BankAccount::copyWithZone, you used the alloc-init and you returned the object which is fine. When implementing Savings::copyWithZone, you call super copyWithZone. What you get is an object of type BankAccount so you cannot use setSavingNumber:andOffer: on it. Since you used the alloc-init in the base class, you should also use the alloc-init and setMethods in the Savings class.
To avoid duplicating code, I'd recomment implementing an initWithBankAccount in BankAccount and the same in Savings.
Then in the copyWithZone, you'd have
return [[BankAccount allocWithZone:zone] initWithBankAccount:self];
and
return [[Savings allocWithZone:zone] initWithSavings:self];
Although you must make sure that in initWithSavings you call either
self = [super initWithBankAccount:savings];
or you call straight init and you copy the base member initialization.
Have a look at Implementing object copy from Memory management programming guide.

Related

Why is it ok instantiatie self from a super init method?

I was doing some coding where I had a class MyClass which inherits from class MySuperClass. MyClass has a property myProperty.
So I was creating an instance of this class from JSON and in a moment of thoughtlessness I wrote my method like this:
+ (instancetype)newFromJSON:(NSDictionary *)json {
MyClass *myObject = [super newFromJSON:json];
myObject.myProperty = someValue;
return myObject;
}
Note that MySuperClass does have a method + (instancetype)newFromJSON:(NSDictionary *)json.
Now, this obviously doesn't work since the call to super newFromJSON will return an instance of MySuperClass which would become the actual type of myObject. This will of course give me a runtime error since MySuperClass doesn't have a myProperty property.
But this got me thinking about something. Why are we able to do seemingly the same thing when we are instantiating objects with a call to [super init]?
Why is it ok to do this:
- (instancetype)init {
self = [super init];
if (self) {
self.myProperty = someValue;
}
return self;
}
Is it because init methods are treated specially in this regard like they are in so many other? Or is it perhaps that assigning to selfchanges the actual type in a way that does not happen when assigning to a regular variable?
The super keyword only indicates from where in the inheritance chain to start looking to find the selector (method) you are invoking. It says to start looking at the current instance's superclass, instead of the instance's class.
What it does not do is change the class type of the self parameter implicitly passed to a method.
Thus, when invoking [super init], the init implementation in the superclass still receives a reference to MySubClass (or whatever).
Note: you can find documentation which states that init may return a different class than the one on which it was invoked. This is common for class clusters. This is because the idiomatic implementation of init simply returns self without constructing a new instance, but it's allowed to.
A few points of clarification:
+ (instancetype)newFromJSON:(NSDictionary *)json {
MyClass *myObject = [super newFromJSON:json];
myObject.myProperty = someValue;
return myObject;
}
When you invoke [super newFromJSON:json], all you are doing is telling the Objective-C runtime to start the search for the method newFromJSON: from self's superclass.
It is not changing the class self.
So, yes, that code is correct and will work fine.
Furthermore, there is absolutely nothing special about the init method and its treatment of super.
There is a bit of a difference in when you are doing + (instancetype)newFromJSON:(NSDictionary *)json versus init. The former is doing both an allocation of memory and initialization of the new instance. init is solely doing the initialization of the instance.
init is special during compilation, in that it does expect you to call [super init] (it will warn you). But effectively it is saying "use my superclass to initialize me first".
Note to do what you want is possible. You just need to have the superclass modify how it allocates memory. You need to do something like:
Parent *myObject = [[[super class] alloc] init];
Here is a code example to hopefully illustrate these points.
Let's say you have these classes:
#interface Parent : NSObject
#property (nonatomic, assign) NSInteger someValue;
+ (instancetype)newInstance;
- (instancetype)init;
#end
#implementation Parent
+ (instancetype)newInstance {
Parent *myObject = [[[super class] alloc] init];
NSLog(#"Creating new item of class %#", NSStringFromClass([myObject class]));
return myObject;
}
- (instancetype)init {
// This [super init] calls NSObject's init
self = [super init];
if (self) {
_someValue = 1000;
}
return self;
}
#end
#interface ClassA : Parent
#property (nonatomic, assign) NSInteger otherValue;
#end
#implementation ClassA
+ (instancetype)newInstance {
ClassA *myObject = [super newInstance];
myObject.otherValue = 2000;
return myObject;
}
- (instancetype)init {
// This [super init] calls ClassA's init
self = [super init];
if (self) {
}
return self;
}
#end
#interface ClassB : Parent
#end
#implementation ClassB
// Default init will be Parent's
#end
#interface ClassC : Parent
#end
#implementation ClassC
- (instancetype)init {
// We are not calling [super init];
// NOTE: This will yield a warning since we are not calling super
return self;
}
#end
If you execute:
ClassA *classA = [[ClassA alloc] init];
ClassB *classB = [[ClassB alloc] init];
ClassC *classC = [[ClassC alloc] init];
Parent *newInstanceParent = [Parent newInstance];
ClassA *newInstanceClassA = [ClassA newInstance];
NSLog(#"classA.someValue = %ld, classB.someValue = %ld, classC.someValue = %ld", classA.someValue, classB.someValue, classC.someValue);
NSLog(#"classA.otherValue = %ld, newInstanceClassA.otherValue = %ld", classA.otherValue, newInstanceClassA.otherValue);
NSLog(#"newInstanceParent is %#, newInstanceClassA is %#", NSStringFromClass([newInstanceParent class]), NSStringFromClass([newInstanceClassA class]));
You'll get output of:
Creating new item of class Parent
Creating new item of class ClassA
classA.someValue = 1000, classB.someValue = 1000, classC.someValue = 0
classA.otherValue = 0, newInstanceClassA.otherValue = 2000
newInstanceParent is Parent, newInstanceClassA is ClassA

Singleton set in AppDelegate loses it value when allocated in another class

I have a iPad application where I'm attempting to use a singleton. This is the code in the .h file:
//-------------------------------------------
//-- singleton: timeFormat
#interface SingletonTimeFormat : NSObject {
}
#property (nonatomic, retain) NSNumber *timeFormat;
+ (id)sharedTimeFormat;
#end
This is the code from the .m file:
//-------------------------------------------
//-- SingletonTimeFormat
#implementation SingletonTimeFormat {
}
#synthesize timeFormat;
//-- sharedColorScheme --
+ (id)sharedTimeFormat {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-id) init {
self = [super init];
if (self) {
timeFormat = [[NSNumber alloc] init];
}
return self;
}
#end
I load the value (either 12 or 24) in AppDelegate - didFinishLaunchingWithOptions, then when I want to get the value of timeFormat I use this:
SingletonTimeFormat *stf = [[SingletonTimeFormat alloc]init];
if([stf.timeFormat isEqualToNumber: [NSNumber numberWithInt:12]]) {
which returns 0 (it was set correctly in AppDelegate, but apparently when I do the alloc in another class, it loses it's value. So obviously it's not working! (I have several other singletons that have the same pattern, but so far they appear to be working.
What's wrong here and how do I fix it?
You don't want to call your singleton using alloc init. With this singleton, all references to it should be through its sharedTimeFormat method, which will init the object if necessary, and will return the singleton instance otherwise.
In other words, it doesn't appear that you're referencing the instance of the object stored in the static sharedObject variable, which means that it's stored value will not necessarily be the same.

global variable in objective C

I'm new in Objective C and stuck on this problem already 5 days)) What i have to do is write implementation for simple task about city and metropolis. I have class City with properties and class metropolis that has an global array which adds city object through createCity method. I have implemented this task but this arrays returns nothing.
Can anybody help me?
Here is part of the task:
1. Write a “City” class, which inherits from NSObject. Your class should contain the following:
Variables:
name, age, population.
Instance methods:
setName:age:population (single method) which set city’s name, age and population. getName, getAge, getPopulation which return city’s name, age and population, respectfully.
nextDay which adds a random number to city’s population, then subtracts a random number from city’s population. Figure out a way to generate random numbers yourself.
2. Create an instance of City class, set its name, age and population as you want.
3. Write a for-­‐loop (if in doubt how to do it – google or use Xcode’s help system) for 10 steps. Each step send ‘nextDay’ message to your object and print out the population.
4. Write a “Metropolis” class. It should contain the following:
Variable:
array of 10 cities.
Instance method:
createCity:atIndex:withPopulation: (single method) which creates a city with first parameter being a name at index (from the second parameter) and sets its population to that of third parameter. So, you should be able to do this:
[myMetropolis createCity: #”Almaty” atIndex: 2 withPopulation: 1500000]
5. Create an instance of Metropolis class and create all 10 cities.
Here is my implementation:
City.h
#import <Foundation/Foundation.h>
#interface City : NSObject
{
NSString* name;
int age;
int population;
}
-(void)setName: (NSString*)n age: (int)a population: (int)p;
-(NSString*)getName;
-(int)getAge;
-(int)getPopulation;
-(void)nextDay;
#end
City.m
#import "City.h"
#implementation City
-(void)setName:(NSString*)n age:(int)a population:(int)p
{
name = n;
age = a;
population = p;
}
-(NSString*)getName
{
return name;
}
-(int)getAge
{
return age;
}
-(int)getPopulation
{
return population;
}
-(void)nextDay
{
int r = arc4random() % 100;
int r2 = arc4random() % 100;
population = population + r;
population = population - r2;
}
#end
Metropolis.h
#import <Foundation/Foundation.h>
#import "City.h"
#interface Metropolis : NSObject{
NSMutableArray* myArray;
}
-(void)createCity: (NSString*)n atIndex: (int)a withPopulation: (int)p;
-(NSMutableArray*) getArray;
#end
Metropolis.m
#import "Metropolis.h"
#import "City.h"
#implementation Metropolis
NSMutableArray* myArray = nil;
- (void)initialize {
myArray = [[NSMutableArray alloc]initWithCapacity:10];
}
-(void)createCity:(NSString*)n atIndex:(int)a withPopulation:(int)p
{
City* newCity = [[City alloc]init];
[newCity setName:n age:0 population:p];
[myArray insertObject:newCity atIndex:a];
}
-(NSMutableArray*)getArray
{
return myArray;
}
#end
main.m
#import <Foundation/Foundation.h>
#import "City.h"
#import "Metropolis.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Metropolis* myMetropolis = [[Metropolis alloc]init];
[myMetropolis createCity:#"Aktobe" atIndex:0 withPopulation:15];
[Metropolis initialize];
NSMutableArray* c = [[NSMutableArray alloc]init];
c = [myMetropolis getArray];
NSLog(#"%#", [[c objectAtIndex:0] getName]);
}
return 0;
}
The method for initialization is -(void)init; this method should be overwritten in your implementation of Metropolis.
You are calling - (void)initialize; which is wrong in this case.
So, simply change - (void)initialize { to -(void)init { in your implementation of Metropolis and delete the line: [Metropolis initialize]; in main.
After the comment below the proper init method should be:
-(id) init {
self = [super init];
if (self) {
myArray = [[NSMutableArray alloc]initWithCapacity:10];
}
return self;
}
I've rewritten my answer to make it more complete, and to incorporate some of the other ideas generated in the other answers, especially #Hannes Sverrisson
The easy way to fix your issue is to call initialize BEFORE createCity (otherwise your trying to add objects to a nil array) and to also make sure you're not calling initialize from a static context. i.e. change [Metropolis initialize]; to [myMetropolis initialize];
The better way, and by better I mean more consistent with typical objective-c design, you should override the instance method init. This is done in the Metropolis implementation and replaces your initialize method.
-(id) init {
self = [super init];
if (self) {
myArray = [[NSMutableArray alloc]initWithCapacity:10];
}
return self;
}
or to make it more fun, create a new init method that takes the number of cities as a parameter.
-(id) initWithNumberOfCities:(NSInteger)numCities {
self = [super init];
if (self) {
myArray = [[NSMutableArray alloc]initWithCapacity:numCities];
}
return self;
}
Then in your main method, remove the call to [Metropolis initialize]. The reason for this is when you say:
Metropolis* myMetropolis = [[Metropolis alloc]init];
or
Metropolis* myMetropolis = [[Metropolis alloc]initWithNumberOfCities:10];
the init method is being called inline after the allocation takes place.
You don't need to write getters or create backing instance variables. You can use Objective-C 2.0's #property syntax.
#property (strong) NSString *name;
#property (assign) NSInteger age;
#property (assign) NSInteger population;
- (void)setName:(NSString*)name age:(NSInteger)age population:(NSInteger)population;
- (void)nextDay;
Then you access the properties using self.name, self.age, self.population or if you need to access the backing variable itself, _name, _age, _population.

Giving each subclass its own copy of a class variable

I have the following class in my iOS application (it is like an abstract class from the Java world).
#implementation WSObject
static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
if (_dictionary == nil) {
_dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
return _dictionary;
}
...
#end
I then have multiple classes which implement this above WSObject with the class method dictionary. The problem is, that each of these classes should have their own _dictionary, but they are all sharing the same object from the super class. I could, of course, copy to all the subclasses, but that would break the reusability. Besides this getter, there are other class methods in WSObject which mutate the dictionary. Because of this, there would be a several class methods which should be in every subclass.
How can I solve this in a smart way? Please tell me if my description is insufficient.
Associative references seem like they'll do the trick. You can essentially tack some storage on to the class object itself. (I'm using NSStrings here, in place of the dictionaries you want to use, just for demonstration.)
Superclass:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface Stuper : NSObject
// Accessor method for the "class variable"
+ (NSString *) str;
// Analog to your +localStorePath
+ (NSString *) quote;
#end
#import "Stuper.h"
// The doc suggests simply using the address of a static variable as the key.
// This works fine, even though every class is (as in your problem) using
// the same key, because we are associating to a different class each time.
static char key;
#implementation Stuper
+ (NSString *) str {
NSString * s = objc_getAssociatedObject(self, &key);
if( !s ){
s = [self quote];
// You'll probably want to use OBJC_ASSOCIATION_RETAIN for your dictionary.
// self inside a class method is the class object; use that as
// the associator. The string is now tied to the associator, i.e.,
// has the same lifetime.
objc_setAssociatedObject(self, &key, s, OBJC_ASSOCIATION_COPY);
}
return s;
}
+ (NSString *) quote {
return #"It was the best of times, it was the worst of times.";
}
#end
Subclass:
#import "Stuper.h"
#interface Stub : Stuper #end
#import "Stub.h"
#implementation Stub
+ (NSString *) quote {
return #"Call me Ishmael.";
}
#end
Trying this out:
#import <Foundation/Foundation.h>
#import "Stuper.h"
#import "Stub.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(#"%#", [Stuper str]);
NSLog(#"%#", [Stub str]);
[pool drain];
return 0;
}
Each class object now has its own string, associated with it.
2011-12-05 23:11:09.031 SubClassVariables[36254:903] It was the best of times, it was the worst of times.
2011-12-05 23:11:09.034 SubClassVariables[36254:903] Call me Ishmael.
The only downside here is that you'll have to call the accessor method every time you want the object; you don't have a pointer you can use directly. You can call objc_getAssociatedObject in the superclass as an accessor, too, of course, since it has access to key.
In order to give each subclass its own dictionary, store a second dictionary object in your primary dictionary using the class name as the key. For example:
static NSMutableDictionary *_dictionary = nil;
+ (NSDictionary*)dictionary
{
if (_dictionary == nil)
_dictionary = [[NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]] mutableCopy];
NSString *key = NSStringFromClass( [self class] );
if ( [_dictionary objectForKey:key] == nil )
[_dictionary setObject:[NSMutableDictionary dictionary] forKey:key];
return [_dictionary objectForKey:key];
}
Perhaps you can return a copy of the dictionary
#implementation WSObject
static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
if (_dictionary == nil) {
_dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
return [_dictionary copy];
}
...
#end
Keep in mind that if you modify _dictionary you will get a copy of that modified dictionary which may differ from what is on disk.
How often is this being called? is it really necessary to cache the file contents in this static _dictionary object?
Why not just fetch it every time form disk, assuming it isn't too often that performance comes into question.
#implementation WSObject
+(NSDictionary*) dictionary {
return [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
...
#end

How to write an Objective-C convenience constructor

I'm trying to add a convenience constructor to my custom object.
Similar to [NSArray arrayWithArray:]
I know it involves a class method that returns an auto released object. I've been googling around but all I can seem to find is the definition of a convenience constructor but not how to write one.
Let's say you have the following:
#class PotatoPeeler : NSObject
- (instancetype)initWithWidget: (Widget *)w;
#end
Then to add a factory method, you'd change it to this:
#class PotatoPeeler : NSObject
+ (instancetype)potatoPeelerWithWidget: (Widget *)w;
- (instancetype)initWithWidget: (Widget *)w;
#end
And your implementation would simply be:
+ (instancetype)potatoPeelerWithWidget: (Widget *)w {
return [[[self alloc] initWithWidget: w] autorelease];
}
Edit: replaced id with instancetype. They are functionally identical, but the latter provides better hints to the compiler about the method's return type.
Generally my approach is the following: first I create a normal initializer method (instance method), then I create a class method that calls the normal initializer. It seems to me Apple uses the same approach most of the time. An example:
#implementation SomeObject
#synthesize string = _string; // assuming there's an 'string' property in the header
- (id)initWithString:(NSString *)string
{
self = [super init];
if (self)
{
self.string = string;
}
return self;
}
+ (SomeObject *)someObjectWithString:(NSString *)string
{
return [[[SomeObject alloc] initWithString:string] autorelease];
}
- (void)dealloc
{
self.string = nil;
[super dealloc];
}
#end