About moving few methods to the superclass - objective-c

I need to move the same method from 4 different classes to the superclass.
Such methods are exactly the same except for the type of a variable declared in them:
For example, in the method in the first class I have
FirstClass var = [[FirstClass alloc] init]
in the second class
SecondClass var = [[SecondClass alloc] init]
and so on.
What's the best way to implement this variation in the superclass ?
Should I use NSClassFromString in the superclass and get each string from each method in the subclasses?
thanks

I'm not 100% sure I get what you mean. So I could be answering the wrong question
If inside your class you need to use an object (I've called it worker below) to do your work, but the class of this object is not known til later, you can use dependency injection (DI).
MyClass.h
#interface MyClass : NSObject
#property (nonatomic, retain) id<WorkerInterface> worker;
#end
MyClass.m
#implementation MyClass
#synthesize worker = _worker;
- (void)myMethod;
{
[self.worker doSomething];
}
// You could also provide a default class to use if one is not passed in
//
// - (id<WorkerInterface)worker;
// {
// if (!_worker) {
// _worker = [[DefaultWorker alloc] init];
// }
// return _worker;
// }
#end
Now whenever I instantiate this class I can simply pass in the appropriate object to be used e.g:
MyWorkerClass *worker = [[MyWorkerClass alloc] init]; // <- Conforms to #protocol(WorkerInterface)
MyClass *instance = [[MyClass alloc] init];
instance.worker = worker;
[instance doSomething];

If all the different types of iVar's you intend on initializing in the subclasses are descended from a common class, then I'd store that class in the super, or else just store it as an id. Then, setup a property accessor in each of your subclasses the casts the iVar as you need it.
#interface superClass : NSObject{
id _superIvar;
}
#end
#implementation superClass : NSObject
....super's code....
#end
Now in the implementation of the subclass declare a property in a category, shown below (or in the interface, if you want it public)
#interface subClass (private)
#property (strong) ClassType *superIvar;
#end;
#implementation
- (void) setSuperIvar:(ClassType *)superIvar{
_superIvar = superIvar;
}
- (ClassType *) superIvar{
return (ClassType *) _superIvar;
}
- (void) someMethodThatUsesSuperIvar{
[self.superIvar doSomething];
}
#end
Alternatively, if you don't want to open your _superIvar to direct access, you can set a property on the superclass and access through the property on the subclass. But in this way you can easily access super's ivars cast to the appropriate type.

Related

I do not understand ways of declaring instance variable in the code

I do not quite understand the way of declaring instance variable and property. Can someone explain in detail the difference of the two codes below? In the second method, if I use _name for instance variable, is it the same function as the way declaring name in first code? Thanks!
First Code:
// OrderItem.h
#import <Foundation/Foundation.h>
#interface OrderItem : NSObject
{
#public NSString *name;
}
-(id) initWithItemName: (NSString *) itemName;
#end
// OrderItem.m
#import "OrderItem.h"
#implementation OrderItem
-(id) initWithItemName: (NSString *) itemName {
self = [super init];
if (self) {
name = itemName;
NSLog(#"Initializing OrderItem");
}
return self;
}
#end
Second Code:
// OrderItem.h
#import <Foundation/Foundation.h>
#interface OrderItem : NSObject
#property (strong,nonatomic) NSString *name;
-(id) initWithItemName: (NSString *) itemName;
#end
// OrderItem.m
#import "OrderItem.h"
#implementation OrderItem
-(id) initWithItemName: (NSString *) itemName {
self = [super init];
if (self) {
_name = itemName;
NSLog(#"Initializing OrderItem");
}
return self;
}
#end
In the first case you have declared an instance variable (usually called an ivar in Objective-C).
In the second case you have declared a property. A property is a set of two methods, a getter and a setter, usually accessed using dot notation, e.g. self.name. However, an ivar is automatically synthesized for the property with the name _name. That instance variable is what you are accessing in your init.
You can actually change the name of the ivar using #synthesize name = _myName or not have it at all (if you declare the getter and setter manually, no ivar will be synthesized).
Objective-C properties are a rather complicated topic so don't worry if you don't understand it immediately.
Properties are public which means that other classes can read and write them (even classes that aren't subclasses of the class that declares the property). In addition to that, properties provide a getter and a setter method (mutator methods). The getter of a property gets called every time you access the property
NSString *aName = self.name;
Whereas the setter is accessed every time you write or assign to a property.
self.name = #"Some name";
Instance variables (or ivars) are, by default, only visible for the class that declares it and its subclasses (also known as being encapsulated by their class). You can change this default behavior when you add the keyword #public to your ivar declaration though.

Variable losing value in delegation pattern

I'm trying to learn about delegation in Objective-C, and am having a minor issue with a variable losing it's data in the transfer process. I have Class1 that contains an NSMutableArray. The array gets populated, then I would like to transfer the array's values to Class2, and display it. Here is the relevant code in Class1:
//Class1.h
#class Class1;
// define the protocol for the delegate
#protocol Class1Delegate
#required
-(void)sayHello:(Class1 *)customClass withAntArray:(NSMutableArray *)antArray;
#end
#interface Class1 : MySuperClassName
#property (nonatomic, assign) id delegate;
-(void)helloDelegate;
#end
//Class1.m:
#interface Class1 ()
#property (nonatomic, strong) NSMutableArray *antOccurenceTimes;
#end
#implementation Class1
#synthesize antOccurenceTimes;
-(void)helloDelegate
{
// send the message to the delegate
[_delegate sayHello:self withAntArray:self.antOccurenceTimes];
}
Now, this is what I have in Class2:
#import "Class1.h"
#interface Class2 : UIView <Class1Delegate>
#end
// Class2.m:
- (void)appropriateTimeToCallMethod {
Class1 *initAntMarks = [[Class1 alloc] init];
initAntMarks.delegate = self;
[initAntMarks helloDelegate];
}
-(void)sayHello:(Class1 *)customClass withAntArray:(NSMutableArray *)antArray {
NSLog(#"Hello! %#", antArray.description);
}
The antArray.description reads as "NULL". Now, I figured that obviously it will be null, because I just created an entirely new instance of the class right before calling upon the needed method. I feel like I may have something mixed up, and being so new to delegation, I'm not sure exactly what. Does anyone know what I need to tweak to utilize Delegation?
I forgot to add that I did initialize it in Class1, and it gets populated just fine. It's only in class2 that this is occurring.
I initalize antOccuranceTimes in a separate method in ClassA in the snippet below, and the NSLog fires twice...
NSLog(#"Array initalized in class A");
antOccurenceTimes = [NSMutableArray new];
Change this line:
#property (nonatomic, assign) id delegate;
to:
#property (nonatomic, weak) id <Class1Delegate> delegate;
assign should only be used for C primitives, not Objective-c object references. You should also be checking if your object actually conforms to the delegate before messaging the delegate.
Edit:
I think you may be confused about the purpose of delegation.
Class1 *initAntMarks = [[Class1 alloc] init];
initAntMarks.delegate = self;
[initAntMarks helloDelegate];
Why are you calling a method on an object which in turn calls a delegate method when you could simply create a method that returns the NSMutableArray? The way you have your code currently set up requires that before the call to -helloDelegate you have to have filled the array with the appropriate objects. The purpose of delegation in MVC is to inform an object about an event that took place inside of another object. You are "delegating" the task off to another object, or you could say, that another object if responsible for the fulfillment of the task. Read the Apple Docs on Delegation. Delegation in your code is not the correct pattern to implement, as I stated you can simply return that array with a method call.
Edit 2:
There are two ways you can achieve this, through property methods or through an explicit method that returns your array. If you choose to use property methods, the property declaration must be in the public interface i.e. the .h file so that your class can all the accessors when the object is being implemented.
//Inside the .h
#property (nonatomic, strong) NSMutableArray *antOccurenceTimes;
This will automatically provide you with two accessor methods for the antOccurenceTimes property. These are the getter -antOccurenceTimes and setter -setAntOccurenceTimes: methods. Now after you initialize the class and fill your array you can call -antOccurenceTimes to return the array.
You can also create an explicit method that return the array:
- (NSMutableArray *)hello{
//Do something here
return _antOccurenceTimes;
}
You have not yet initialized the antOccurenceTimes. Of cause it is nil. There are many options depending on what you need. You can, for example, initialize it in a init function:
- (instancetype)init {
self = [super init];
if( self ) {
antOccurenceTimes = [NSMutableArray array];
[antOccurenceTimes addObject:#"Hello World"];
}
}
Or maybe initialize it before you call the delegate the function.
-(void)helloDelegate
{
// send the message to the delegate
self.antOccurenceTimes = [NSMutableArray array];
[self.antOccurenceTimes addObject:#"Hello World"];
[_delegate sayHello:self withAntArray:self.antOccurenceTimes];
}
I think you get my point.

Enforce initializing superclass's ivar after calling superclass's init method

I need to enforce the initialization of an ivar in a superclass but that ivar usually can not be initialized without other data in the subclass to be initialized. The two solutions I have thought of is:
pass the required generated key for the ivar to the superclass's init method
calling a second superclass method from the subclass's init method
Here is example (contrived, non-working) code. The stringBasedOnSubclassKey ivar should be initialized to the NSString from the subclass's key method.
#interface MySuperclass : NSObject
#property (nonatomic, readonly) NSString *stringBasedOnSubclassKey;
#end
#interface MySubclass : MySuperclass
#property (nonatomic, assign, readonly) int value;
#end
#implementation MySubclass
- (instancetype)init
{
if (self = [super init]) {
_value = 30;
}
return self;
}
- (NSString *)key
{
return [NSString stringWithFormat:#"UniqueKey-%d", self.value];
}
So the question is is there a way to enforce the initialization of the stringBasedOnSubclassKey ivar using the return value of the "key" method? I don't believe I can enforce solution 1 and 2 above. These subclasses may also be created by other outside developers so the key method may be more complicated than this.
Update: I am dealing with existing subclasses of this base class so solutions limiting the changes to existing subclasses is a factor.
Write the getter for stringBasedOnSubclassKey in such a way as to force initialization of it:
- (NSString *) stringBasedOnSubclassKey {
if !(_stringBasedOnSubclassKey) {
_stringBasedOnSubclassKey = // whatever;
}
return _stringBasedOnSubclassKey;
}
And write the superclass key method to throw an exception, thus forcing the client to override it in the subclass.

How can I assign values to other class variable in objective-c

The below coding is working and I can see the values in my second screen. But I am using the same in other classes with different variables in this format. But it dosent show me the variable if after i type the classname with a dot. I cant figure this out. Is there any way to pass values to other class.
InstallProfiler_2 *installProfiler2 = [[InstallProfiler_2 alloc] initWithNibName:#"InstallProfiler_2" bundle:nil];
installProfiler2.profilerType2 = profilerType;
[self.navigationController pushViewController:installProfiler2 animated:NO];
[installProfiler2 release];
Make sure that:
You have imported the class header.
The #property declarations are in this header and not a class extension.
#property refers to ivars so when you say
if after i type the classname with a dot
this terminology is incorrect, you probably mean after you start typing the name of the variable which has points to an instance of a class.
ClassA.h
#interface ClassA : NSObject
#property (nonatomic, weak) NSInteger myInt;
#end
ClassA.m
#implementation ClassA
#synthesize myInt = _myInt;
#end
ClassB.m
#import "ClassA.h" // <- Import the header of the class
# implementation ClassB
// .. other methods and stuff
- (void)myMethod;
{
ClassA *instanceOfClassA = [[ClassA alloc] init]; // <- Working with an instance not a class
instanceOfClassA.myInt = 1;
}
#end
UPDATE
Make sure your #property () does not have readonly between the round brackets.
Also make sure you have either #synthesize'd the ivar in the implementation or have provided both a getter and a setter for the ivar.
Failing that show some relevant code so we can actually see what your doing - we are answering pretty blindly here.
The dot syntax is only available with property/synthesize
Create a custom setter/getter:
+ (BOOL)awesomeClassVar {
return _classVar;
}
+ (void)setAwesomeClassVar:(BOOL)newVar {
_classVar = newVar;
}
then call as a method from the other class:
BOOL theOtherClassVar = [AwesomeClass awesomeClassVar];
[AwesomeClass setAwesomeClassVar:!theOtherClassVar];

In Objective-C, what happens to the original object when the init method sets self?

I have three Objective-C classes:
#interface ClassA : NSObject{
IBOutlet id<ClassAProtocol>delegate
//other instance variables
}
//methods
#end
#interface ClassB : ClassA{
//instance variables
}
//methods
#end
#interface ClassC : ClassA{
//instance variables
}
//methods
#end
My objective is so that when an instance of ClassA is called for in either code or InterfaceBuilder, an instance of ClassB or ClassC is actually created. Whether it will be ClassB or ClassC depends on a return value of a method in ClassAProtocol as implemented by the delegate object.
#implementation ClassA
static BOOL _initFromSubclass = NO;
-(id)init{
if(_initFromSubclass){
_initFromSubclass = NO;
self = [super init];
}else {
_initFromSubclass = YES;
if([delegate shouldInitClassB]){
self = [[ClassB alloc] init];
}else{
self = [[ClassC alloc] init];
}
}
return self;
}
//other methods
#end
This doesn't work the way I wanted because at the init call, the delegate (set in Interface Builder) is still nil, and so the object created is always ClassC. Also, a ClassA object is created first, then, in its init call, creates a new ClassC object, with a different memory address, and no ClassA object is dealloced. I have three questions:
1)What happens to the original ClassA object? (I think it's leaked, but I want to know).
2)How do I avoid the leak?
3)How do I accomplish what I actually want? By the time the delegate is set (say, in awakeFromNib method), it's too late to reset the object.
Yes I think it will be leaked because it has a retain count of +1 after the alloc but nothing will release it.
You can use [self release] before reassigning the value of self. Some argue that it should be [self dealloc] directly, but I personally prefer the former.
Objects instantiated from nibs are sent initWithCoder: messages I think, so override that instead of init. Although, at this point, I'm still not sure whether delegate will be set.