Setting Other Class Variable - objective-c

I have 2 classes, ClassA and ClassB
ClassA has one BOOL variable set to No.
I am trying to set this variable to Yes from ClassB, but can't seem to figure out how to.
Below is the code I am using which doesn't work, it is simply what I would've thought would work, I have stripped out the unnecessary information:
Class A:
ClassA.h
#interface AppDelegate : NSObject <NSApplicationDelegate> {
BOOL boolean;
}
- (id) init;
ClassA.m
- (id) init {
boolean = NO;
}
Class B:
ClassB.h
#import "ClassA.h"
- (IBAction) setBoolean: (id)sender;
ClassB.m
- (id) init {
ClassA * theClassA = [[ClassA alloc] init];
return self;
}
- (IBAction) setBoolean: (id)sender {
[theClassA boolean] = YES;
}
I hope this makes sense. I simply want to set the BOOL boolean in ClassA to YES from ClassB.

You can't assign a property like that ([object property] = value). The proper syntax is [object setProperty:value] or object.property = value.
I wouldn't call a variable boolean. Might be misleading. Even though it's not the keyword for a boolean variable in Objective-C it is in a lot of other languages.
And you have to return the initialized object (self) in your init method (you have an id return type, not void):
- (id) init {
self = [super init];
if (self) {
boolean = NO;
}
return self;
}
Also, you didn't specify an instance variable for theClassA in your ClassB implementation. You just create a local object and then leak it (you don't release it). Instead, declare it in your ClassB.h:
#class ClassA;
#interface ClassB : NSObject {
ClassA *theClassA;
}
- (IBAction)setBoolean:(id)sender;
#end
Then initialize it like this:
- (id) init {
self = [super init];
if (self) {
theClassA = [[ClassA alloc] init];
}
return self;
}
And don't forget to release it in dealloc:
- (void)dealloc {
[theClassA release];
[super dealloc];
}
And one last thing. Having a method - (IBAction) setBoolean: (id)sender in your ClassB implies that ClassB has a property called boolean, which is not the case. I recommend renaming that method and/or rethinking your class designs.

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

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

making class members properties and synthesizing them

Is it safe to say that if a class member does not need getter or setter functions then there's no point in making them properties and synthesizing them?
Well, yes, but often properties can be helpful in the implementation itself even if the properties won't be set outside of the implementation.
For example, suppose you had
#interface SomeObject : NSObject {
NSThing *thing;
}
#end
#implementation SomeObject
- (id)init {
if((self = [super init]))
thing = [[NSThing someThing] retain];
return self;
}
- (void)someMethod {
if(thing)
[thing release];
thing = [[NSThing someOtherThing] retain];
}
// etc etc
#end
Why would you want to bother having to check if thing had been allocated, release thing, set it to something else, and then retain it again, when you could simply do:
- (id)init {
if((self = [super init]))
[self setThing:[NSThing someThing]];
return self;
}
- (void)someMethod {
[self setThing:[NSThing someOtherThing]];
}
If you don't want to make these properties accessible outside of your class, you can use a category
#interface SomeObject ()
#property (retain) NSThing *thing;
#end
in your .m file.

View of custom class

I have the following class:
#interface Gamer {
...
}
+(id) CreatePlayer;
#end
#implementation Gamer
+(id) CreatePlayer
{
return [[[self alloc] init]autorelease];
}
#end
I need to use the Gamer in an another class as instance variable.
For example like this:
#interface Layer{
Gamer * mCenterGamer;
}
#end
#implementation
-(void) init{
mCenterGamer = [Gamer CreatePlayer];
}
-(void) exampleFuncForUseGamer{
[mCenterGamer ...]// some methods of the Gamer class
}
#end
Is it correct? (I think autorelease freed the mCenterGamer after exiting from the init function)
You need to retain mCenterGamer (and make sure to release it in the Layer's -dealloc method). Also, -init needs id as its return type:
- (id)init {
if (self = [super init])
mCenterGamer = [[Gamer CreatePlayer] retain];
return self;
}
- (void)dealloc {
[mCenterGamer release];
[super dealloc];
}
Your -exampleFuncForUseGamer should be fine, depending on what you're trying to do there.

Objective-C :: using a method to change an object

I have a class called "CardSet", containing an NSMutableArray* cardSet to hold "cards", which I extend to make "DeckCards". I'd like "CardSet" to have a method called "(void)addCard:(Card*)" (and similarly a method "removeCard"). I'd like "addCard" to some how have access to and set cardSet. Even better I'd like to use the "addCard" method to initialise cardSet. The class file "CardSet.h" reads:
#import < Cocoa/Cocoa.h >
#import < Card.h >
#interface CardSet : NSObject {
NSMutableArray* cardSet;
}
-(id)init;
-(NSMutableArray*)getCardSet;
-(void)setCardSet:(NSMutableArray *)new_cardset;
-(Card*)getCard:(NSInteger) index;
**-(void)addCard:(Card*) new_card;**
-(void)removeCard:(Card*) old_card;
-(void)dealloc;
#property (readwrite, retain, getter=getCardSet, setter=setCardSet) NSMutableArray* cardSet;
#end
and the method file reads:
#import "CardSet.h"
#implementation CardSet
-(id)init{
if( self = [super init] ){} //will add initialisations here later
return self;
}
-(NSMutableArray*)getCardSet{
return cardSet;
}
-(void)setCardSet:(NSMutableArray *)new_cardSet{
cardSet = new_cardSet;
}
-(Card*)getCard:(NSInteger)index{
return [cardSet objectAtIndex:index];
}
**-(void)addCard:(Card *)new_card{
[cardSet addObject:new_card];
}**
-(void)removeCard:(Card *)old_card{
[cardSet removeObject:old_card];
}
-(void)dealloc{
[cardSet release];
[super dealloc];
}
#synthesize cardSet;
#end
This compiles just fine. I'd like to initialise a "DeckCards" instance using its "addCard" method 52 times. When I call addCard 52 times in a DeckCards setter method, and ask for the size of its "cardSet", I'm returned 0.
This appears to be a scope or privileges problem? Can the "addCard" method have any setter privileges? Must a setter argument be the same as the return and respective member type?
[I can work around the above by creating an NSMutableArray object "deck_cards_temp" outside of "DeckCard", add 52 cards to this, and pass it to set the member of my "DeckCards" instance via the setter inherited from "CardSet". This is not very satisfactory!]
What do you advise? Many thanks in advance for your help and patience.
You are never actually creating the cardSet object. You should be creating it in your -init method:
-(id)init
{
if( self = [super init] )
{
cardSet = [[NSMutableArray alloc] init];
}
return self;
}
Because you never actually create the array, all the calls to -addCard: are being sent to a nil object.
When you pass in an array to -setCardSet:, you are passing in an initialized array so the array is no longer nil and the -addCard: calls work fine.
CardSet.h
#import <Cocoa/Cocoa.h>
// For know we just need to know there is a class named "Card" being used but implemented later
#class Card;
#interface CardSet : NSObject {
NSMutableArray *cardSet;
}
// Here are the methods according to "correct" naming conventions
- (Card *)cardAtIndex:(NSInteger)index;
- (void)addCard:(Card *)card;
- (void)removeCard:(Card *)card;
// This will help us and forget about writing the setter/getter
#property (nonatomic, retain) NSMutableArray *cardSet;
#end
CardSet.m
#import "CardSet.h"
// Now we tell the compiler what "Card" is and what methods etc. it has
#import "Card.h"
#implementation CardSet
#synthesize cardSet;
- (id)init {
if (self = [super init]) {
// If we don't create the cardSet, how are we able to work with it!?
NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.cardSet = anArray;
[anArray release];
}
return self;
}
- (Card *)cardAtIndex:(NSInteger)index {
return [cardSet objectAtIndex:index];
}
- (void)addCard:(Card *)card {
[cardSet addObject:card];
}
- (void)removeCard:(Card *)card {
[cardSet removeObject:card];
}
- (void)dealloc {
[cardSet release];
[super dealloc];
}
#end
As Abizern already noted: Naming the array the same as your class is a bad thing.
I would shorten that init method:
- (id)init {
if (self = [super init]) {
// If we don't create the cardSet, how are we able to work with it!?
self.cardSet = [NSMutableArray array];
}
return self;
}