Subclassing and inheritance in objective-c - iphone

I have a class called AbstractBook, which has a property:
#property(strong) AbstractPage *page;
Now say I have a subclass of AbstractBook called WhiteBook and a subclass of AbstractPage called WhitePage. I want the WhiteBook class to inherit the page object, but in this class, I want it to be the specific WhitePage class instead of AbstractPage.
So, in WhiteBook, I just redeclared the property as WhitePage:
#property(strong) WhitePage *page;
However, I am getting a warning: WhitePage is incompatible with AbstractPage inherited from AbstractBook. What is the right way to implement what I'm going for?

What you want to do is fundamentally impossible. Don't forget that this:
#property(strong) AbstractPage *page;
declares a getter and a setter.
Generally speaking (this is for OO programming generally, not just Objective-C), there is no problem with an override getter returning a subclass of the return type of the super class implementation because this does not break the API contract. In your case, an instance of WhitePage is also an AbstractPage.
However, the setter is different. You can't restrict the type of the parameter because your subclass must be useable anywhere where an AbstractBook is used, so code that invokes the setter on something that it thinks is an AbstractBook is entitled to pass in an instance of any AbstractPage subclass, because that is what the API says.
I think what I would do is add a method to AbstractBook called something like
-(bool) isLegalPage: (AbstractPage) aPage;
that returns true always in the base class but true only for white pages in WhiteBook. Then I would manually implement the setter as follows:
-(void) setPage: (AbstractPage*) aPage
{
if (![self isLegalPage: aPage])
{
// throw an exception or do other error notification
}
else
{
Do the assignment according to reference count/ARC/GC model
}
}
Document that the setter will throw an exception if the page is not legal and that people should use the isLegalPage: method to test this.
The alternative id to have a read only property and use different setters in the base class and subclass, or don't have a setter at all in the base class.

There is no property type covariance in Objective C, so if you want a property returning a subclass of AbstractPage, you need to define a separate property, say whitePage, in your WhiteBook subclass. You can return the same value from your page property, too, and it would work. Moreover, if your users call methods on the AbstractPage*, they would not even need to cast it to WhitePage*.

Remember to program to the interface not implementation.
======================
Option: 1
If your Whitebook truly is a descendant of AbstractBook you don't need to make WhiteBook have a new property called "page", since your AbstractBook already declares it your WhiteBook gets it for free.
As long as your WhitePage inherits from AbstractPage you should be able to cast your page object as a WhitePage. Then you get all original functionality of AbstractPage, plus that of WhitePage.
So you shouldn't need to re-declare page in Whitebook. If you go to your Whitebook.h file and delete this line
#property(strong) WhitePage *page;
Then go into your Whitebook.m file and type self.page you shouldn't get any compile warnings. Then you can cast page as WhitePage and you should be good to go.
WARNING: This means you will need to cast page as a WhitePage where ever you use it, which is not ideal.
======================
Option: 2
You may also try leaving the code as you have it, but make sure to synthesize the "page" prop again in WhiteBook.
======================
Option: 3
You may also try leaving the code as you have it, but make sure to use #dynamic for the "page" prop in WhiteBook. Then implement the getter yourself and return a WhitePage instead of an AbstractPage.

How to override a superclass' property with more specific types?
Normally if you use #synthesize in your AbstractBook class and #dynamic in your WhiteBook class, it should work fine (unless, of course, you use the AbstractPage synthesized method to assign a non-WhitePage to page).

Related

Calling methods from another class in objective-c

I know usually, when you want to call a method on another object, you do:
NewObject *object = [NewObject alloc]init];
[object callMethod];
But I created a class that isn't an object itself meaning it doesn't have properties or memory management. It has a couple methods that calculate some stuff.
From any other class, all I have to do is import the header for this class and do:
#import "MyClass.h"
[MyClass callMethod];
Why in this case do I not have to alloc init? It works just fine.
It sounds like you are trying to call a class method. These are methods which have been defined as:
+(void) myStaticMethod;
instead of
-(void) myMethod;
The plus sign indicates that the method does not use any fields, and thereby does not need to instantiate the object.
In your example, "object" is an instance of a class "NewObject" which has been allocated memory and initialized. Where-as your example, "MyClass" is only a class which because it has static members declared as above, does not need to be instantiated.
Class methods provide a nice way to combine a bunch of related functions into one place, rather than having them spread out in the regular namespace, as would usually be done in straight C. You can also have both class methods and instance methods in the same class, using the class ones when needed, and instantiating the class to use the instance ones when needed.
EDIT: Changed terminology to refer to class methods instead of static methods.
because you are calling a class method. You only need to alloc init objects. Classes only need to be included but not alloc inited. So you don't need to init an NSString class, say.
Edit:
Let's just have some nonsense examples:
+ (void)classMethod {
NSLog("Hi!");
}
[SomeClass classMethod]; // prints Hi!
- (void)instanceMethod { // (say it's an instance method of NSString)
NSLog(self);
}
[#"someNSString" instanceMethod]; // prints someNSString. But you need to have a string first, otherwise you cannot use this method.
There is a difference between "instance" methods (normal ones), that have to be called on an object and have access to self, and "class" methods (called static, in many programming languages), that are invoked on the class and thus do not have a self.
Class methods are similar to C++ static methods, in that they can be invoked without creating a concrete instance of the class. The usefulness of this is you can call a class's specialized factory methods to create a new instance; or, you can define a utility library under the scope of a class that may or may not provide concrete instances depending on the task.
Look at NSDate and NSNumber are good examples of this in the Foundation framework.

Do I have to declare a category for all private methods of a class?

Suppose I want to have a private method of a class to be visible to the implementation of that class only so that the class' interface exposes only what needs to be exposed. In some recent versions of Xcode, I can do this simply by omitting the method's declaration in the header. However, one good book suggests that I should also declare a category in the implementation file and declare that method within it, like this:
// [in MyClass.m]
#import "MyClass.h"
// category declaration - is it really required?
// edit: it's a class extension declaration, I know, but it doesn't change much
#interface MyClass ()
- (void)myPrivateMethod;
#end
#implementation MyClass
- (void)myPrivateMethod
{
// do something
}
// all methods here can call myPrivateMethod
#end
Indeed it does compile and work. Moreover, it does compile and work fine without the category/extension declaration part. This rises a natural question:
If I have a bunch of private methods, do I need to declare all of them in a category? What would be the best practice?
This is not a category, it's a class extension (note that there is no category name, the parentheses are empty).
It is a very good idea to add your private methods to a class extension. Doing so ensures that all methods have declarations, and that these declarations are visible only to the parts of your code where you want them to be visible.
Note that unlike ordinary categories, class extensions let you add instance variables. This is very convenient when your implementation relies on classes that are not part of the interface of your class.

Which is the best way to define private methods for a class in Objective-C?

Like:
#interface ClassXXName(private)
- (void) xxxfunctions
#end
or user category methods?
#interface Foo() creates a class extension (I stand corrected, props to bbum) on interface Foo which is like additional methods added to the interface. Some people also use #interafce Foo(Private) (category) instead of a class extension with (). It's more like "injecting" new methods into a class from outside the class.
Placing this in the .m file just keeps other things from "seeing it" in the .h file, but that's it. Basically people normally use categories or class extensions in .m files to specify private interfaces, but they are also used for things like UIKit uses categories to add row and section public methods to NSIndexPath. (This can be confusing.)
You don't really need to define private methods this way, but if you have a method called bar that calls method foo before foo is defined in the source file you'll get a compiler warning something like "object self may not respond to foo". You can get rid of that by defining foo before you define bar or any other foo-calling code. It's the same with plain C and functions.
Like Ole says this doesn't stop anyone from calling the private methods, it just declares your intention that they be private and causes the compiler to generate the "may not respond to" warnings even if they import the .h file.

objective c categories and inheritance

If a method is defined in both a class and a category on that class, it is undefined which implementation will be called.
But how does this interact with inheritance? Specifically:
Given a superclass category method and a regular method in the subclass, is it guaranteed that the subclass implementation will win when called on a member of the subclass?
Given a superclass regular method and a subclass category method trying to override it, is it guaranteed that the subclass category implementation will win when called on a member of the subclass?
Given a superclass category method and a subclass category method, is it guaranteed that the subclass category method will win when called on a member of the subclass?
Lets just put it this way. Don't override methods using categories, period, ever, end of answer.
If a method is defined in both a class and a category on that class,
it is undefined which implementation will be called.
That is incorrect; the category method will always win. What won't work, though, is if you have multiple categories that implement the same method, then the "which one wins" is undefined.
It is generally "last loaded wins", but that really isn't a hard rule, either.
Note that since many classes will internally have their implementation dividing across categories for code organization purposes, you can't rely on the first rule anyway.
In short, what Joshua said; Do not override methods using categories.
Beyond the inheritance reasons, you are also viciously breaking encapsulation when you do so. It isn't that a category based implementation overrides an existing method, it entirely replaces it. Thus, if you don't reproduce every last internal implementation detail, including bugs, your replacement won't quite work right and debugging it will be hard.
From what I test
Given a superclass category method and a regular method in the
subclass, is it guaranteed that the subclass implementation will win
when called on a member of the subclass? => subclass wins
Given a superclass regular method and a subclass category method
trying to override it, is it guaranteed that the subclass category
implementation will win when called on a member of the subclass? =>
subclass category wins
Given a superclass category method and a subclass category method,
is it guaranteed that the subclass category method will win when
called on a member of the subclass? => subclass category wins
Take a look at the Test category and subclass

Is it good style to declare methods in .h when they're intended to be overwritten by subclass?

I have a class which is intended to be abstract. This means: When someone subclasses it, a few methods MUST be overwritten.
But on the other hand, those methods are not intended to be called manually from anywhere except inside the abstract class (the superclass of the subclass).
Must I declare these methods in .h anyways or can I just add comments in .h which say "you must overwrite -foo and -bar"? Or is there a better pattern to make abstract methods?
Related: Is there a way to create an abstract class in Objective C?
Objective-C doesn't actually have a way to declare a class as abstract. From Apple's Docs:
Abstract Classes
Some classes are designed only or
primarily so that other classes can
inherit from them. These abstract
classes group methods and instance
variables that can be used by a number
of different subclasses into a common
definition. The abstract class is
typically incomplete by itself, but
contains useful code that reduces the
implementation burden of its
subclasses. (Because abstract classes
must have subclasses to be useful,
they’re sometimes also called abstract
superclasses.)
Unlike some other languages,
Objective-C does not have syntax to
mark classes as abstract, nor does it
prevent you from creating an instance
of an abstract class.
The NSObject class is the canonical
example of an abstract class in Cocoa.
You never use instances of the
NSObject class in an application—it
wouldn’t be good for anything; it
would be a generic object with the
ability to do nothing in particular.
The NSView class, on the other hand,
provides an example of an abstract
class instances of which you might
occasionally use directly.
Abstract classes often contain code
that helps define the structure of an
application. When you create
subclasses of these classes, instances
of your new classes fit effortlessly
into the application structure and
work automatically with other objects.
So to answer your question, yes, you need to place the method signature in the header, and should implement the method in the base class such that it generates an error if called, like the related question's answer states.
You can also use a protocol to force classes to implement certain methods.
However you choose to implement the base class, clearly document in the header, as well as in your documentation, exactly what the class assumes and how to go about sub-classing it correctly.
Whenever possible write your code so that improper implementations fail to compile. If you cannot do that then you should try to generate a runtime error (at the very least in a debug build) if the subclass is not written correctly. Do not rely on comments because people will not read them.
You must declare your "protected" and "abstract" methods in a header file, but you can use separate categories to clearly indicate their purpose and intended use.
#interface MyBaseClass : NSObject {
}
- (void)foo;
#end
#interface MyBaseClass(ProtectedMethods)
- (void)bar;
#end
#interface MyBaseClass(AbstractMethods) // Subclasses must implement
- (void)internalBar;
#end
You can put everything in a single header, or you could put your protected and abstract declarations in a separate "protected" header, say MyClassProtected.h, meant to be included only by your subclass implementations. It depends on how badly you want "hide" your protected methods.
Your base class can log, assert, or throw when an abstract/pure-virtual method is called.
As other people have said, Objective-C does not support pure virtual classes.
You can enforce pure virtual behaviour at runtime though. The cleanest way to do this is by using the Objective-C runtime's _cmd and NSObject's -doesNotRecognizeSelector:
- (void)iMustBeImplementedInaSubclass;
{
[self doesNotRecognizeSelector:_cmd]; // Pure virtual
}
As ben says you are probably better served by using a protocol to get your API design right.