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++
Related
i was a beginner in iOS developing.
i was so confused that i get this error.i can simply solve it by changing "[self maxRank]" to "[PlayingCard maxRank]"
but i don't why this happen.
thanks in advance.
this is my code
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit=_suit;
+ (NSArray *)validSuits
{
return #[#"♠︎",#"♣︎",#"♥︎",#"♦︎"];
}
+ (NSArray *)validRanks
{
return #[#"?",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank
{
return [[PlayingCard validRanks] count]-1;
}
- (NSString *)suit
{
return _suit?_suit:#"?";
}
- (void)setSuit:(NSString *)suit
{
if ([[PlayingCard validSuits] containsObject:suit]) {
_suit=suit;
}
}
- (void)setRank:(NSUInteger)rank
{
if (rank<=[self maxRank]) { // this is where i get my error
_rank=rank;
}
}
- (NSString *)contents
{
NSArray *rankString=[PlayingCard validRanks];
return [rankString[self.rank] stringByAppendingString:self.suit];
}
#end
#import "Card.h"
#interface PlayingCard : Card
#property (strong, nonatomic)NSString *suit;
#property (nonatomic)NSUInteger rank;
+ (NSArray *)validSuits;
#end
In Objective-C there are two different types of methods:
1. Class Method - denoted by a + before it
Class methods operate on the class itself. Therefore, when you use self in a class method, it refers to the class.
2. Instance Method - denoted by a - before it
Instance methods operate on a specific instance of a class that has been allocated. Therefore, when you use self in an instance method, it refers to the instance of that class.
Your setRank: method is an instance method but maxRank is a class method. When you try to call maxRank on self from setRank:, you are trying to call an instance method named maxRank, which does not exist. If you want to call a class method without specifying the class explicitly, you can use the class property on all instances:
- (void)instanceMethod {
[self.class maxRank];
}
The maxRank method is a class method, not an instance method, so you cannot use "self" with it. If you want maxRank to be a instance method, you need to change the leading + sign to a hyphen (-).
It's because methods with + prefix are class methods, not instance method.
You need to read up on the difference between those.
Say I had a function that does something with a class:
- (void)doSomethingWithClass:(Class)class
{
[class doSomething];
}
but of course the method + (void)doSomething isn't defined for all classes. Let's say it's defined only for subclasses of the SomeClass class. How can I "restrict" the type of class of the class parameter that's given to the function? I want to be able to do something like this:
- (void)doSomethingWithClass:(/* type of [SomeClass class] */)class
{
[class doSomething];
}
so that I don't have to resort to something like this:
- (void)doSomethingWithClass:(Class)class
{
if (![class isSubClassOfClass:[SomeClass class]])
{
// error
}
[class doSomething];
}
Is this possible to do in Objective-C, and is it ever even a good idea to do something like this?
Class is really a c struct that contains metadata about a class type. Structs do not have inheritance.
You can restrict a method by passing in the instance of the object type instead of the Class struct type.
- (void)doSomethingWithClass:(SomeClass *)sender
{
Class class = [sender class];
[class doSomething];
}
If your method really must accept a Class object as the parameter, then isKindOfClass: or isSubclassOfClass is the way to go.
You should use Protocol, to create a reuse interface for your classes. If you classes conform this protocol, so it will implement method doSomething.
#protocol ClassProtocol
+ (void)doSomething;
#end
#interface ClassEngine : NSObject
#end
#implementation ClassEngine
- (void)doSomethingWithClass:(Class)class {
if([class conformsToProtocol:#protocol(ClassProtocol)])
[(Class<ClassProtocol>)class doSomething];
}
}
#end
It's not possible to specify such a type in Objective-C. But it's possible in Swift -- SomeClass.Type
I have an objective-C singleton as follows:
#interface MyModel : NSObject
+ (MyModel*) model;
...
+ (MyModel*) model
{
static MyModel *singlton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^ {
singlton = [[MyModel alloc] initSharedInstance];
});
return singlton;
}
- (MyModel*) initSharedInstance
{
self = [super init];
if (self)
etc.
}
Which gets called in multiple places within the GUI code as:
[[MyModel model] someMethod];
And therefore the model will get created as a consequence of whichever part of the GUI happens to reference it first.
I'm not sure how to implement the equivalent of accessing the class via [[MyModel model] someMethod] in Swift as all examples of using Swift involve creating an object using an initializer and when Objective C class method code is converted to Swift initializer code there is a problem with it not working when the method does not have parameters.
UPDATE
++++++++++
The workaround below is only necessary if you name your singleton method with a name derived from the suffix of the class name i.e. the OPs question the method name is model and the class is called MyModel.
If the method is renamed to something like singleton then it is possible to call it from Swift just like this:
let m = MyModel.singleton()
+++++++++++
I don't know if this is good/bad practice but I was able to get around the problem with initializer conversion not working when there are no parameters by adding a dummy init method. So using the code from the other answer as an example:
#interface XYZThing : NSObject
+ (XYZThing*) thing;
+ (XYZThing*) thingWithFoo:(int)foo bar:(int)bar;
#end
#implementation XYZThing
+ (XYZThing*) thing
{
NSLog(#"This is not executed");
return nil;
}
+ (XYZThing*)thingWithFoo:(int)foo bar:(int)bar
{
NSLog(#"But this is");
return nil;
}
#end
...
let thing = XYZThing()
let otherThing = XYZThing(foo:3, bar:7)
With this code above the thing method is not called, but the thingWithFoo:bar: method is.
But if it is changed to this then now the thing method will get called:
#interface XYZThing : NSObject
+ (XYZThing*) init;
+ (XYZThing*) thing;
+ (XYZThing*) thingWithFoo:(int)foo bar:(int)bar;
#end
#implementation XYZThing
+ (XYZThing*) init
{
return nil;
}
+ (XYZThing*) thing
{
NSLog(#"Now this is executed");
return nil;
}
+ (XYZThing*)thingWithFoo:(int)foo bar:(int)bar
{
NSLog(#"And so is this");
return nil;
}
#end
...
let thing = XYZThing()
let otherThing = XYZThing(foo:3, bar:7)
If the Swift compiler mistakenly identifies a method as a class factory method, you can use the NS_SWIFT_NAME macro, passing the Swift signature of the method to have it imported correctly. For example:
+ (id)recordWithQuality:(double)quality NS_SWIFT_NAME(record(quality:));
so,your method should be this:
+ (MyModel*)model NS_SWIFT_NAME(log());
Do exactly what the compiler warning tells you to:
MyModel().someMethod()
Read on to see why...
Swift automatically recognizes ObjC conventions for initializers and convenience constructors. If you have a class that looks like this:
#interface XYZThing : NSObject
+ (instancetype)thing;
+ (instancetype)thingWithFoo:(int)foo bar:(int)bar;
#end
...then, when Swift turns them into initializers, it elides the part of the method name that's the generic name of the class (Thing/thing), moves the part of the selector that refers to the parameter to be a parameter label, and drops any prepositions connecting those parts. So the initializer declarations look like this in Swift:
class XYZThing: NSObject [
init()
init(foo: Int, bar: Int)
}
and you construct objects like this:
let thing = XYZThing()
let otherThing = XYZThing(foo:3, bar:7)
A followup: because class methods like +[XYZThing thing] are treated like initializers by the ObjC to Swift translator (even if that doesn't seem to fully work right now), that naming pattern is a bad idea for singletons. A singleton retrieval method shouldn't be an initializer, because an initializer always creates a new instance.
A singleton retrieval method should instead have a name that doesn't start with the generic name of the class; e.g. +sharedThing, +defaultThing, +oneThingToRuleThemAll, etc.
I always confusing to when i used of instance method and class method in programming. Please tell me difference between instance method and class methods and advantages of one another.
All the other answers seem to have been caught out by the incorrect tag that has now been fixed.
In Objective-C, an instance method is a method that is invoked when a message is sent to an instance of a class. So, for instance:
id foo = [[MyClass alloc] init];
[foo someMethod];
// ^^^^^^^^^^ This message invokes an instance method.
In Objective-C, classes are themselves objects and a class method is simply a method that is invoked when a message is sent to a class object. i.e.
[MyClass someMethod];
// ^^^^^^^^^^ This message invokes a class method.
Note that, in the above examples the selector is the same, but because in one case it is sent to an instance of MyClass and in the other case it is sent to MyClass, different methods are invoked. In the interface declaration, you might see:
#interface MyClass : NSObject
{
}
+(id) someMethod; // declaration of class method
-(id) someMethod; // declaration of instance method
#end
and in the implementation
#implementation MyClass
+(id) someMethod
{
// Here self is the class object
}
-(id) someMethod
{
// here self is an instance of the class
}
#end
Edit
Sorry, missed out the second part. There are no advantages or disadvantages as such. It would be like asking what is the difference between while and if and what are the advantages of one over the other. It's sort of meaningless because they are designed for different purposes.
The most common use of class methods is to obtain an instance when you need one. +alloc is a class method which gives you a new uninitialised instance. NSString has loads of class methods to give you new strings, e.g. +stringWithForma
Another common use is to obtain a singleton e.g.
+(MyClass*) myUniqueObject
{
static MyUniqueObject* theObject = nil;
if (theObject == nil)
{
theObject = [[MyClass alloc] init];
}
return theObject;
}
The above method would also work as an instance method, since theObject is static. However, the semantics are clearer if you make it a class method and you don't have to first create an instance.
If we don't want to create the object of class then we use the class method
if we want call the method through object of a class then we use the instance method
I don't know if we can talk of any advantage, this is rather a matter of what you are implementing.
Instance methods apply on instances of classes, so they need an object to be applied on and can access their caller's members:
Foo bar;
bar.instanceMethod();
On the other hand class methods apply on the whole class, they don't rely on any object:
Foo::classMethod();
Static member functions are informally called class methods (incorrectly). In C++ there are no methods, there are member functions.
Read up on the static keyword, that pretty much covers it.
MSDN:
http://msdn.microsoft.com/en-us/library/s1sb61xd.aspx
Google search:
http://www.google.ch/search?aq=f&sourceid=chrome&ie=UTF-8&q=static+keyword+c%2B%2B
Class methods are used with classes but instance methods are used with objects of that class i.e instance
//Class method example
className *objectName = [[className alloc]init];
[objectName methodName];
//Instance method example
[className methodName];
instance methods use an instance of a class, whereas a class method can be used with just the class name. + sign is used before the Class Method where as single desh (-) is used before the instance variable.
#interface MyClass : NSObject
+ (void)aClassMethod;
- (void)anInstanceMethod;
#end
They could also be used like so,
[MyClass aClassMethod];
MyClass *object = [[MyClass alloc] init];
[object anInstanceMethod];
or another example is:
[
NSString string]; //class method
NSString *mystring = [NSString alloc]init];
[mystring changeText]; //instance Method
Like most of the other answers have said, instance methods use an instance of a class, whereas a class method can be used with just the class name. In Objective-C they are defined thusly:
#interface MyClass : NSObject
+ (void)aClassMethod;
- (void)anInstanceMethod;
#end
They could then be used like so:
// class methods must be called on the class itself
[MyClass aClassMethod];
// instance method require an instance of the class
MyClass *object = [[MyClass alloc] init];
[object anInstanceMethod];
Some real world examples of class methods are the convenience methods on many Foundation classes like NSString's +stringWithFormat: or NSArray's +arrayWithArray:. An instance method would be NSArray's -count method.
-(NSDictionary *)properties;
+(NSDictionary *)ClassProperties;
Now, how can I call ClassProperties from sub-classes?
-(NSDictionary *)properties {
return [? ClassProperties];
}
The point is that ClassProperties gets the list of properties in the class, so i can't call the base class definition.
Along the lines of Marc's response, you could more generally call the method with
[[self class] ClassProperties]
In fact, if your base class and all the subclasses implement + (NSDictionary *)ClassProperties, then your base class can do this
- (NSDictionary *)properties {
return [[self class] ClassProperties];
}
and then none of your subclasses will need to know about - (NSDictionary *)properties. The correct class method would be called based on what self is.
You can just use the class name of the subclass.