Everything I know about programming says that instance methods can call class methods, but class methods cannot call instance methods.
This post agrees...
Call instance method from class method
Yet miraculously the class method sharedInstance manages to call instance method init. What am I missing ??
static iRpDatabase *sharedDatabase;
#implementation iRpDatabase
{
}
+(iRpDatabase*)sharedInstance
{
if(sharedDatabase == nil)
{
sharedDatabase = [[self alloc] init];
}
return sharedDatabase;
}
// this is an instance method, called from class method above.
-(id)init
{
if (self = [super init]) {
someInstanceVariable = XYZ;
[self someInstanceMethod];
}
return self;
}
The statement that a class method can't call instance methods means that the class method can't call instance methods on self since self represents the class, not an instance of the class.
In the sharedInstance method you are calling an instance method but it is being called on a specific instance of the class. That's fine.
Think of this example:
+ (void)someClassMethodOfiRpDatabase {
NSString *str = #"Hello";
NSInteger len = [str length]; // look - I called an instance method
}
This example in no different than your sharedInstance method question. It's fine to call instance methods on a specific instance of an object, even if you happen to be in some class method.
Related
As we all know, factory methods can't call instance methods. Why does the code below work?
// .m file implementation DemoClass
// custom instance init method
- (instancetype)initWithDate:(NSDate *)date {
if (self = [super init]) {
self.lastTime = date;
}
return self;
}
// custom factory method
+ (instancetype)DemoClassWithDate:(NSDate *)date
//here calling instance method initWithDate:
return [[self alloc] initWithDate:date];
}
[self alloc] will return an instance. initWithDate is just an instance method. No reason why a class method wouldn't be allowed to call an instance method on an instance.
PS. I would highly recommend that you check your compiler settings and tell the compiler to give you a warning if the result of '=' is used as a boolean value. This will prevent many hard to find bugs. You'll have to change the if to
if ((self = [super init]) != nil)
Because it has a reference to the, newly created, instance:
return [[self alloc] initWithDate:date];
// ^^^^^^^^^^^^
// reference
My header class looks like:
#import "Card.h"
#interface PlayingCard : Card
#property (strong, nonatomic) NSString *suit;
#property (nonatomic) NSUInteger rank;
+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;
#end
And my implementation:
#implementation PlayingCard
+ (NSArray *) validSuits
{
return #[#"♥︎", #"♣︎", #"♦︎", #"♠︎"];
}
+ (NSArray *) rankStrings
{
return #[#"?", #"1", #"2", #"3", #"4"];
}
- (void)setSuit:(NSString *)suit
{
if ([[PlayingCard validSuits] containsObject:suit])
{
_suit = suit;
}
}
- (NSString *)suit
{
return _suit ? _suit : #"?"; // if suit !nil return suit, else return ? string.
}
+ (NSUInteger)maxRank
{
return [[self rankStrings] count] - 1;
}
#end
So I understand that any method with a + means it's a Class method.
My question is, why must I use [PlayingCard classMethod] e.g. [PlayingCard validSuits] in the setSuit method whereas I can use [self classMethod] e.g. [self rankStrings] in the maxRank method?
I'm assuming it's something to do with the maxRank method being a class method whereas setSuit isn't. But could it be because setSuit is a setter?
I really don't know, I can't visualise what's going on here. I've only just started my foray into Objective-C and am coming from a Java background.
I have realised I can substitute PlayingCard in for self in the maxRank method without any error messages, however substituting self in for PlayingCard in the setSuit method gives me an error saying
No visible #interface for 'PlayingCard' declares the selector for 'validSuits'
Any explanation as to why this is the case and what's going on would be great. Thanks!
The meaning of self in methods
Every Objective-C method receives an implicit self argument. Instance methods receive the instance, while class methods receive the class object (remember: classes are objects).
If you want to send a class method, the compiler lets you use two types of syntax:
[ClassName classMethod]
[classObjectPtr classMethod]
The first syntax is used in [PlayingCard maxRank]. Here, the target is (explicitly) the PlayingCard class.
A class method already has a class object as a target for sending class methods: the self argument. So they can use [self classMethod] to send other class methods.
Why sending a message to self in class methods?
The advantage of the latter is that the class is not explicitly named. This makes it possible to override class methods in subclasses and call them from base classes.
You basically get the same dynamic method dispatch as with instance methods. This is actually a nice feature of Objective-C not present in Java or C++.
Instance methods would use the dynamic version by accessing their class and sending the message to that:
- (void)setSuit:(NSString *)suit
{
if ([[[self class] validSuits] containsObject:suit])
{
_suit = suit;
}
}
Now an imaginary subclass of PlayingCard could override the class method validSuits and implicitly alter the behavior of setSuit:.
self can be an instance or a class depending on the type of method declared.
- (void)setSuit: is an instance method, thus self is an instance inside this method declaration.
+ (NSUInteger)maxRank is a class method, thus self is a class inside inside this method declaration.
+ (void)classMethod;
- (void)instanceMethod;
- (void)setSuit
{ // self is an instance here
[self classMethod]; // warning, class method sent to instance
[self instanceMethod]; // works, instance method sent to instance
}
+ (NSUInteger)maxRank
{ // self is a class here
[self classMethod]; // works, class method sent to class
[self instanceMethod]; // warning, instance method sent to class
}
You tried to called a "class method" on self inside an instance method where self in an "instance".
In a class method, self refers to the class (it refers to an object that represents the class that obj-c runtime creates for you), so you can use it to call class level method.
In an instance method, self refers to the instance. If you want to call class level method in an instance method, you need to use the class name instead.
Java analogy of obj-c class methods is the static method. Java's this keyword is similar to self, except it can't be used to refer to a class.
setSuit is an instance method, and validSuits is a class method. However, both maxRank and rankStrings are class methods. Class methods are basically the same as static methods in C++
I am from Actionscript Background. In Actionscript Class Method can access only Class Methods and Class properties.
But In Objective C,
How Class method gameResultAll can access Instance Method initFromPlist
+(NSMutableArray *)gameResultAll://Class Method
-(id)initFromPlist:(id)plist;//Instance Method
NSMutableArray *gameResults = [GameResult gameResultAll]; // (returns GameResult array)
Why [self init] method is called instead of [super init] to create an instance from class method.
Thanks in advance.
#import "GameResult.h"
#implementation GameResult
#define GAME_RESULT_KEY #"gameresult_key"
#define SCORE_KEY #"score"
+(NSMutableArray *)gameResultAll
{
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
for (id plist in [[[[NSUserDefaults standardUserDefaults] dictionaryForKey:GAME_RESULT_KEY] mutableCopy] allValues])
{
GameResult *gameResult = [[GameResult alloc] initFromPlist:plist];
[resultArray addObject:gameResult];
}
return resultArray;
}
//Designated initialiser
-(id)initFromPlist:(id)plist
{
self = [self init];
if(self)
{
if([plist isKindOfClass:[NSDictionary class]])
{
NSDictionary *resultDictionary = (NSDictionary*)plist;
_score = (int)resultDictionary[SCORE_KEY];
}
}
return self;
}
You asked:
How Class method gameResultAll can access Instance Method initFromPlist
It can access that method because you used the alloc method, which creates an instance of GameResult. Now that you have an instance, you can use instance methods in conjunction with this instance.
By the way, this is a very common pattern, a "convenience" class method that allocates an instance of an object (with alloc) and initializes the object (with init or some permutation of that). Or, as in this case, it can create an array of these objects.
You then go on to ask:
Why [self init] method is called instead of [super init] to create an instance from class method.
I can understand the confusion, but there is an important, yet subtle distinction in the behavior of these two.
Imagine this scenario:
At some future date, you subclass GameResult, e.g. ArcadeGameResult;
You implemented an init method for ArcadeGameResult that initializes some properties unique to this subclass; and
You happen to initialize a ArcadeGameResult instance like so:
ArcadeGameResult *agr = [[ArcadeGameResult alloc] initFromPlist:plist];
Because the initFromPlist uses [self init], it means that the the initFromPlist method of GameResult will end up calling the init method of the object (which in this example, is actually a ArcadeGameResult object). But if initFromPlist in GameResult called [super init] instead, it would not have called ArcadeGameResult's init method and thus initFromPlist would be problematic if ever used in conjunction with a subclass.
Bottom line, unless the method you're calling is the exact same method signature, it's safer to call the self rendition rather than the super rendition. It's a little more flexible in case you ever decide to subclass in the future.
There is a corollary to the counsel. When calling class methods from an instance method, you should refer to [self class] rather than the class name. So, imagine your GameResult class had a class method:
+ (void)someClassMethod
{
// do something
}
If you had some GameResult instance method that was going to avail itself of this method, you might be tempted to write:
- (void)someInstanceMethod
{
// do some stuff
[GameResult someClassMethod];
}
But that's not a good idea. You would instead use the following:
- (void)someInstanceMethod
{
// do some stuff
[[self class] someClassMethod];
}
They look very similar, but the latter lets you implement a someClassMethod in a subclass, and this instance method will still work. If you use the former construct, the subclassed class method wouldn't be called by someInstanceMethod.
These are subtle issues, and probably not critical for your current code sample. But hopefully it illuminates the choice of [self init] versus [super init] in this situation.
In Actionscript Class Method can access only Class Methods and Class properties.
That's not different in Objective-C either (because nothing else would make sense), so:
How Class method GameResultAll can access Instance Method initFromPlist
Only through a valid instance.
Why [self init] method is called instead of [self super] to create an instance from class method.
Because the latter is a syntax error, perhaps? Read a basic Objective-C tutorial.
I want to mock an instance method for all instances of a class using OCMock however I don't have the instance of the class to override it rather it is created inside the method that I'm testing.
So my question is: is it possible to override this method for all instances of a class or will I need to inject that instance into the method rather than create it inside the method?
i.e.
[[ClassThatHasTheInstanceMethodToOverride andCall:#selector(callThisMethodInstead) onObject:self] someInstanceMethod];
I got there in the end with this set of methods:
Method originalMethod = nil;
Method swizzleMethod = nil;
#import <objc/runtime.h>
....
- (void) swizzleInstanceMethodForInstancesOfClass:(Class)targetClass selector:(SEL)selector
{
originalMethod = class_getInstanceMethod(targetClass, selector);
swizzleMethod = class_getInstanceMethod([self class], selector);
method_exchangeImplementations(originalMethod, swizzleMethod);
}
- (void) deswizzle
{
method_exchangeImplementations(swizzleMethod, originalMethod);
swizzleMethod = nil;
originalMethod = nil;
}
I have two classes, both inherited from NSObject. One is called class1, the other is called class2. They both each have one instance, class1->obj1, and class2->obj2.
In class1's init method, I make the instance of class2. From obj2's init method I want to call the instance method -(void)methode from obj1
How do I do this (what are delegates, could I use one)?
- initWithThingy:(Class1 *)anObj
{
self = [super init];
if (self) {
obj1 = [anObj retain]; // or use your set method/property, if you have one
[obj1 methode];
}
return self;
}