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.
Related
I have a base class lets say BaseClass which does some logic and handles gestures. I have another class FooBarClass which provides the view and is also a subclass of BaseClass, (FooBar : Base).
I know that I can call methods in super class by super methodName. I am stuck in a situation now, all of views are designed like these and now I need to pass message from FooBar to Base.
Is that possible ? If so how ? Should I be using NSNotifications or is there any better way to do it ?
If you are creating instance of subclass, which in your case is FooBarClass, you need not worry about message passing from super class to subclass. With inheritance, whatever properties, methods are exposed in header file (.h) of BaseClass, can be accessed from FooBarClass. If the methods belonging to BaseClass has been overridden in FooBarClass, then you have to explicitly make use of super otherwise, you can directly call self. However, if the properties belonging to BaseClass has been overridden in FooBarClass, then that variable will be holding the value which has been stored last. That is the reason why usually, properties are never overridden as it gets confusing.
Lastly, there is no need for NSNotification.
Ex: BaseClass.h
#interface BaseClass : UIView
- (void)runTest;
- (void)sayHi;
- (void)sayHi2;
#property (assign, nonatomic) NSInteger commonVar;
#end
BaseClass.m
- (void)runTest
{
self.commonVar = 100;
}
- (void)sayHi
{
NSLog(#"Hi from super");
NSLog(#"In super variable = %d", self.commonVar);
}
- (void)sayHi2
{
NSLog(#"Hi from super2");
}
FooBarClass.h
#interface FooBaseClass : BaseClass
#property (assign, nonatomic) NSInteger commonVar;
#end
FooBarClass.m
- (void)runTest
{
self.commonVar = 1;
[super runTest]; // Now, commonVar variable will be holding 100 throughout.
[super sayHi];
[super sayHi2]; // Same as next line because there is no sayHi2 overridden.
[self sayHi2];
[self sayHi];
}
- (void)sayHi
{
NSLog(#"Hi from derived");
NSLog(#"In derived variable = %d", self.commonVar);
}
Hope this answer helps you.
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++
How do I prevent a particular class from being subclassed?
I am not aware of such functionality (say final keyword for example) in the language. However Apple says it has done so for all classes in AddressBookUI.framework (in iOS)
For educational purposes, how can I achieve the same functionality, or how would they have done such thing?
From iOS7 Release Notes(Requires login) :
Here's one way: override allocWithZone: from within your "final" class (substituting MyFinalClassName for your actual class name) like this:
+ (id)allocWithZone:(struct _NSZone *)zone
{
if (self != [MyFinalClassName class]) {
NSAssert(nil, #"Subclassing MyFinalClassName not allowed.");
return nil;
}
return [super allocWithZone:zone];
}
This will prevent a subclass that is not a member of MyFinalClassName from being alloc'ed (and therefore init'ed as well), since NSObject's allocWithZone: must be called eventually, and by refusing to call super from your "final" class, you will prevent this.
There's a simpler way to prevent subclassing in Xcode 6 as a result of Swift interop. To prevent Swift classes from being subclassed in Objective-C the objc_subclassing_restricted is added to all class definitions in the {ProjectName}-Swift.h file.
You can use this in your projects:
#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
# define FOO_FINAL __attribute__((objc_subclassing_restricted))
#else
# define FOO_FINAL
#endif
FOO_FINAL
#interface Foo : NSObject
#end
#interface Bar : Foo
#end
The compiler will halt on the definition of Bar with Cannot subclass a class with objc_subclassing_restricted attribute
Here is possible solution:
#interface FinalClass : NSObject
#end
#implementation FinalClass
- (id)init
{
if (self.class != [FinalClass class]) {
return nil;
}
self = [super init];
if (self) {
// instance initialization
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
#end
#implementation InvalidSubclass
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
#end
I'm not sure this is 100% guaranteed because it's runtime-checking anyway, but it should be enough to block and warn people that they should not subclass this. Subclass might skip superclass's init, but then the instance will not be usable because it's not fully initialised by superclass.
Something like the following will ensure that every time an "impossible subclass" calls +alloc, an object will be allocated that is an instance of FinalClass, and not the subclass. This is essentially what NSObject's +alloc method does, but here we specify an explicit class to create. This is how NSObject allocates instances (in Obj-C 2), but there is no guarantee this will always be the case, so you may want to add an appropriate -dealloc which calls object_dispose. This method also means you don't get a nil object back if you try to instantiate a subclass - you do get an instance of FinalClass.
#interface FinalClass: NSObject
//...
+ (id)alloc; // Optional
#end
// ...
#import <objc/runtime.h>
#implementation FinalClass
+ (id)alloc {
if (![self isMemberOfClass:[FinalClass class]]) {
// Emit warning about invalid subclass being ignored.
}
self = class_createInstance([FinalClass class], 0);
if (self == nil) {
// Error handling
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
// Anything not in FinalClass will not work as +alloc will
// create a FinalClass instance.
#end
Note: I'm not sure I'd use this myself - specifying that a class shouldn't be subclassed is more in the nature of a design-contract with the programmer rather than an enforced rule at compile- or runtime.
Is there a way to call a class method from another method within the same class?
For example:
+classMethodA{
}
+classMethodB{
//I would like to call classMethodA here
}
In a class method, self refers to the class being messaged. So from within another class method (say classMethodB), use:
+ (void)classMethodB
{
// ...
[self classMethodA];
// ...
}
From within an instance method (say instanceMethodB), use:
- (void)instanceMethodB
{
// ...
[[self class] classMethodA];
// ...
}
Note that neither presumes which class you are messaging. The actual class may be a subclass.
Should be as simple as:
[MyClass classMethodA];
If that's not working, make sure you have the method signature defined in the class's interface. (Usually in a .h file)
In objective C 'self' is used to call other methods within the same class.
So you just need to write
+classMethodB{
[self classMethodA];
}
Sure.
Say you have these methods defined:
#interface MDPerson : NSObject {
NSString *firstName;
NSString *lastName;
}
+ (id)person;
+ (id)personWithFirstName:(NSString *)aFirst lastName:(NSString *)aLast;
- (id)initWithFirstName:(NSString *)aFirst lastName:(NSString *)aLast;
#property (copy) NSString *firstName;
#property (copy) NSString *lastName;
#end
The first 2 class methods could be implemented as follows:
+ (id)person {
return [[self class] personWithFirstName:#"John" lastName:#"Doe"];
}
+ (id)personWithFirstName:(NSString *)aFirst lastName:(NSString *)aLast {
return [[[[self class] alloc] initWithFirstName:aFirst lastName:aLast]
autorelease];
}
I see that the UIColor class can call the variable like this [UIColor redColor];
How can I write my class to do the same thing? Also, can I have a method only for class, for example, like this:
[MyClass callingMyMethod];
Thank you.
Yes. Just use a + instead of a - when declaring the method:
+ (void)callingMyMethod
{
...
}
You have to create a class method. Class methods are defined like instance method, but have a + instead of a -:
#interface MyClass : NSObject
+ (id)classMethod;
- (id)instanceMethod;
#end
#implementation MyClass
+ (id)classMethod
{
return self; // The class object
}
- (id)instanceMethod
{
return self; // An instance of the class
}
Note that within a class method, the self variable will refer to the class, not an instance of the class.
Yes, they are call class messages. Use + instead of - when defining a message.
Like this:
#interface MyClass : NSObject
{
}
+ (void) callingMyMethod;
Use a + instead of the -. This is called a class method and is used to for initializing and return the object.
#interface SomeClass : NSObject
+ (id)callingMyMethod;
- (id)otherMethod;
#end
#implementation SomeClass
+ (id)callingMyMethod
{
return self; // The class object
}
- (id)otherMethod
{
return self; // An instance of the class
}