UITableViewController init vs. initWithStyle - objective-c

the class ref says:
If you use the standard init method to initialize a UITableViewController object, a table view in the plain style is created.
i do not understand, where this behaviour comes from - i would like to see i in some code
or api but
UITableViewController has no init in its api
how could some base class' init know about a suitable default style for a derived class?
thanks for every hint

Every object has an init method, but a lot of classes have a so-called designated initializer. That is the main initializer, and the others are merely convenience methods calling that designated initializer.
According to that doc, in this case the init method probably looks something like this:
- (id)init
{
return [self initWithStyle:UITableViewStylePlain];
}
Methods from a superclass do not appear in the documentation of the derived class, except if the derived class overrides it and has something important to say about it. That's why you don't see init documented in UITableViewController, it's part of NSObject from which UITableViewController derives (through UIScrollView -> UIView -> UIResponder -> NSObject).
As for the second part of your question: a base class can (should) never know anything about derived classes. A derived class that wants a different default style simple overrides init again.

in UITableViewController.m
- (id) init
{
return [self initWithStyle:UITableViewStylePlain];
}
The init method will call the designated initializer.

Related

Beginner Objective-C: Extending a class?

I'm very new with objective-C, and I am trying to implement a 'factory' style class extending a cocos2D class called CCSprite.
I want there to be a class Monster which extends CCSprite that will create and issue sprites with unique IDs so that I may call and manipulate the generated sprites later on; I am extending CCSprite to add an instance_id property and a static global_id variable.
I want to be able to use all the CCSprite constructors such as spriteWithImageNamed along with all the other functions CCSprite has, the problem is that my instance_id isn't being assigned/incremented properly(It's always 0), and I don't really know where to start to fix this.
Here is what I have:
Monster.h:
#import "CCSprite.h"
#interface Monster : CCSprite
#property (nonatomic, readonly) int instance_id;
#end
Monster.m:
#import "Monster.h"
static int global_id = 0;
#implementation Monster:CCSprite
#synthesize instance_id;
-(id) init{
self = [super init];
if(self){
instance_id = global_id;
global_id++;
}
return self;
}
#end
Is the init function called every time any constructor is called? (eg spriteWithImageNamed)?
How do I ensure my extended properties and variables are applied when I call the parent class's functions?
In Objective-C, a class's designated initializer is the one that other initializers are supposed to call into. For many classes, this is indeed init. According to the Cocos2D documentation, however, init is not the designated initializer for CCSprite. Instead, the designated initializer is:
- (id)initWithTexture:(CCTexture *)texture rect:(CGRect)rect rotated:(BOOL)rotated
So that is the method you need to override if you want all the other initializers to call your version.
(If you want to confirm for yourself that this works, you can put a log statement or breakpoint in your implementation of the designated initializer, then call other initializers and make sure the designated initializer is called.)
Your init method may or not be called depending on what init these factory methods are using internally. For example, initWithFile may call self = [super init] instead of self = [self init], in which case your implementation will not be called. You can ensure that your initialization is always performed by overriding all init methods of all superclasses and do your necessary setup. Also keep in mind that you can also override any of the factory methods, where you can get the object from calling super, and then assign it's id property before returning.
looking at CCSprite it seems that every factory method like +(id)spriteWith* calls an initWith* method
and every initWith* method at the end of the chain calls [self init]
so if you override the init method as you do in monster.m you are ok!
by the way, if you want to create your sprite using a factory method do not call [CCSprite spriteWith*], call [Monster spriteWith*] instead
best regards

Can subclass override non-public methods

I have two classes: BatchDownloader, SpeechDownlader
BatchDownloader is the base class, and SpeechDownloader inherited it.
In BatchDownloader, whenever one file was downloaded, -(void)downloadComplete:task will be called.
But in SpeechDownloader, I also want to post a notification in downloadComplete:task.
Can I just write the method with the same name in SpeechDownloader's implementation ? or there is a better way ?
Thanks.
p.s. I don't want to make -(void)downloadComplete:task public, because it should only be called by itself.
If you implement a method in a subclass that has the same name as a private method in a superclass, your subclass method will be called on instances of your subclass.
i.e., if you implement a method in your superclass like this, without declaring it anywhere:
#implementation classA
- (void)doSomething {
NSLog("a");
}
Then, in your subclass implementation, implement a method with the same name:
#implementation subclassOfA
- (void)doSomething {
NSLog("b");
}
When you call doSomething on an instance of your subclass, the subclass implementation will be called instead of the superclass implementation, so the code in this example will result in "b" being printed to the console.
However, if you also want to access the superclass implementation of the method, you can use:
- (void)doSomething {
[super doSomething];
NSLog("b");
}
This will also call the superclass implementation. If you get a compile error (due to the method being private and super not appearing to implement it), you can use [super performSelector:#selector(doSomething)] instead to do exactly the same thing.
This happens because of the way the Objective-C runtime looks up method calls. Since these methods have exactly the same method signature (same name, return type and arguments [none]), they are considered equal, and the runtime always checks the class of the object before looking in superclasses, so it will find the subclass method implementation first.
Also, this means you can do this:
classA *test = [subclassOfA new];
[test doSomething];
And, surprise surprise, the console will print "b" (Or "a b" if you called the super implementation too).
If you implement the method with the same method signature it will be called faith your implementation, public or not.

How to avoid subclass inadvertently overriding superclass private method

I'm writing a library, which will potentially be used by people that aren't me.
Let's say I write a class:
InterestingClass.h
#interface InterestingClass: NSObject
- (id)initWithIdentifier:(NSString *)Identifier;
#end
InterestingClass.m
#interface InterestingClass()
- (void)interestingMethod;
#end
#implementation InterestingClass
- (id)initWithIdentifier:(NSString *)Identifier {
self = [super init];
if (self) {
[self interestingMethod];
}
return self;
}
- (void)interestingMethod {
//do some interesting stuff
}
#end
What if somebody is using the library later down the line and decides to create a subclass of InterestingClass?:
InterestingSubClass.h
#interface InterestingSubClass: InterestingClass
#end
InterestingSubClass.m
#interface InterestingSubClass()
- (void)interestingMethod;
#end
#implementation InterestingSubClass
- (void)interestingMethod {
//do some equally interesting, but completely unrelated stuff
}
#end
The future library user can see from the public interface that initWithIdentifier is a method of the superclass. If they override this method, they'll probably assume (correctly) that the superclass method should be called in the subclass implementation.
However, what if they define a method (in the subclass private interface) which inadvertently has the same name as an unrelated method in the superclass 'private' interface? Without them reading the superclass private interface, they won't know that instead of just creating a new method, they've also overridden something in the superclass. The subclass implementation may end up getting called unexpectedly, and the work that the superclass is expecting to be done when calling the method will not get done.
All of the SO questions I've read seem to suggest that this is just the way that ObjC works and that there isn't a way of getting around it. Is this the case, or can I do something to protect my 'private' methods from being overridden?
Alternatively, is there any way to scope the calling of methods from my superclass so I can be sure that the superclass implementation will be called instead of a subclass implementation?
AFAIK, the best you can hope for is declaring that overrides must call super. You can do that by defining the method in the superclass as:
- (void)interestingMethod NS_REQUIRES_SUPER;
This will compile-time flag any overrides that don't call super.
For framework code a simple way to deal with this is to just give all of your private methods a private prefix.
You'll often notice in stack traces that the Apple frameworks call private methods often starting with an under bar _.
This would only really be a real concern if you are indeed providing a framework for external use where people can not see your source.
NB
Don't start your methods with an under bar prefix as this convention is already reserved

Recommended instance variables initialization place for Cocoa Touch classes

I'm feeling very stupid, but...what is the right way to init instance variables in custom types, derived from Cocoa Touch UI classes?
Say I have type, derived from UIViewController, let it be TRUIController.
I defined an ivar as follows:
#implementation TRUIController
{
NSNumberFormatter *_numberFormatter;
}
#end
And where should I put _numberFormatter initialization code, if I want it to be executed before any UI methods like viewDidLoad, etc?
In other languages I would create constructor, call base constructor and then init my ivars.
But that simply does not work in objective-c and Cocoa Touch.
In case described above,
If I write
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibBundleOrNil bundle:nibBundleOrNil];
if(self)
{
//init ivars
}
return self;
}
It won't work, I'll try other initWithBlaBla methods, and finally find init, which is actually called, in that case - initWithCoder:
Now, let's say I have class, derived from UITableViewCell. Again, need to init NSNumberFormatter ivar. Why can't I just override init:, call super and init my ivars?
What's the idea behind this strange design decision not to have single common initialization method for all types?
Is it the only way to initialize ivars in derived types? Every time do some research to figure out what method to override this time?
Have I missed something? Because it feels extremely stupid/unintuitive/error-prone
You have to do so overriding the designated initializer.
From the official documentation
A designated initializer is an init method of a class that invokes an
init method of the superclass. (Other initializers invoke the init
methods defined by the class.) Every public class should have one or
more designated initializers. As examples of designated initializers
there is NSView’s initWithFrame: and NSResponder’s init method. Where
init methods are not meant to be overridden, as is the case with
NSString and other abstract classes fronting class clusters, the
subclass is expected to implement its own.
Designated initializers should be clearly identified because this
information is important to those who want to subclass your class. A
subclass can just override the designated initializer and all other
initializers will work as designed.
When you implement a class of a framework, you often have to implement
its archiving methods as well: initWithCoder: and encodeWithCoder:. Be
careful not to do things in the initialization code path that doesn’t
happen when the object is unarchived. A good way to achieve this is to
call a common routine from your designated initializers and
initWithCoder: (which is a designated initializer itself) if your
class implements archiving.
Read the documentation for finding out which initializer is the designated one and override it.
According to the documentation of UIViewController initWithNibName:bundle: is the designated initializer for this class is so you should override that one, but if you are not programmatically instantiating your class (e.g. you're loading it from nib/storyboard) you have to override the archiving method initWithCoder:.
As suggested by the documentation you can create a routine for initialization and call it from both the designated initializer and the archiving method.
It generally makes sense to use the designated initializer as a starting point, but if you are not sure this is always the one that will be used that won't make all that much sense.
Personally I therefore prefer to create a setup method which is called from any init-method I know is likely to be used at some stage. For ViewControllers I normally call the setup method from viewDidLoad instead (unless of course there's any state that's needed before this stage).

Swizzling a single instance, not a class

I have a category on NSObject which supposed to so some stuff. When I call it on an object, I would like to override its dealloc method to do some cleanups.
I wanted to do it using method swizzling, but could not figure out how. The only examples I've found are on how to replace the method implementation for the entire class (in my case, it would override dealloc for ALL NSObjects - which I don't want to).
I want to override the dealloc method of specific instances of NSObject.
#interface NSObject(MyCategory)
-(void)test;
#end
#implementation NSObject(MyCategory)
-(void)newDealloc
{
// do some cleanup here
[self dealloc]; // call actual dealloc method
}
-(void)test
{
IMP orig=[self methodForSelector:#selector(dealloc)];
IMP repl=[self methodForSelector:#selector(newDealloc)];
if (...) // 'test' might be called several times, this replacement should happen only on the first call
{
method_exchangeImplementations(..., ...);
}
}
#end
You can't really do this since objects don't have their own method tables. Only classes have method tables and if you change those it will affect every object of that class. There is a straightforward way around this though: Changing the class of your object at runtime to a dynamically created subclass. This technique, also called isa-swizzling, is used by Apple to implement automatic KVO.
This is a powerful method and it has its uses. But for your case there is an easier method using associated objects. Basically you use objc_setAssociatedObject to associate another object to your first object which does the cleanup in its dealloc. You can find more details in this blog post on Cocoa is my Girlfriend.
Method selection is based on the class of an object instance, so method swizzling affects all instances of the same class - as you discovered.
But you can change the class of an instance, but you must be careful! Here is the outline, assume you have a class:
#instance MyPlainObject : NSObject
- (void) doSomething;
#end
Now if for just some of the instances of MyPlainObject you'd like to alter the behaviour of doSomething you first define a subclass:
#instance MyFancyObject: MyPlainObject
- (void) doSomething;
#end
Now you can clearly make instances of MyFancyObject, but what we need to do is take a pre-existing instance of MyPlainObject and make it into a MyFancyObject so we get the new behaviour. For that we can swizzle the class, add the following to MyFancyObject:
static Class myPlainObjectClass;
static Class myFancyObjectClass;
+ (void)initialize
{
myPlainObjectClass = objc_getClass("MyPlainObject");
myFancyObjectClass = objc_getClass("MyFancyObject");
}
+ (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy
{
object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass);
}
Now for any original instance of MyPlainClass you can switch to behave as a MyFancyClass, and vice-versa:
MyPlainClass *mpc = [MyPlainClass new];
...
// masquerade as MyFancyClass
[MyFancyClass changeKind:mpc fancy:YES]
... // mpc behaves as a MyFancyClass
// revert to true nature
[MyFancyClass changeKind:mpc: fancy:NO];
(Some) of the caveats:
You can only do this if the subclass overrides or adds methods, and adds static (class) variables.
You also need a sub-class for ever class you wish to change the behaviour of, you can't have a single class which can change the behaviour of many different classes.
I made a swizzling API that also features instance specific swizzling. I think this is exactly what you're looking for: https://github.com/JonasGessner/JGMethodSwizzler
It works by creating a dynamic subclass for the specific instance that you're swizzling at runtime.