I have a class Foo that implements an +(void)initialize method. I also have a class that's a subclass of Foo. When I instantiate the subclass, the initialize method also gets called on Foo which I don't want. How do I prevent this?
Thanks.
In your scenario (when there are subclasses involved) you should check the class to which the initialize method is sent:
+ (void) initialize
{
if ( self == [MyClass class] )
{
// Do something here only once
}
}
You'll need to implement + (void)initialize in your subclass as well. Usually people call [super initialize], but you'll want to skip that step. An empty method will prevent Foo's from being called.
EDIT The superclasses initialize method is always called. It can't and shouldn't be prevented by subclassing, because technically the superclass is initialized too and could be used independently.
I've solved it by not implementing initialize and just calling a setup method instead
Related
In Objective-C, how do I call an object's super class method?
For example, lets say I have an instance of an object "foo".
"foo" has a method that is overriden. I do not want to call this overriden method. Instead, I want to call the original method on the foo object instance.
You cannot do:
[[instance super] super_method]; as far as I am aware. So how can I go about doing this?
I know I can add a method to foo:
-(void) callsuper
{
[super super_method];
}
and do:
[foo callsuper];
Any other ways? I really don't want to create a method just to do that.
The keyword super does this for you. It is seen very commonly in -init methods. Here is an example.
- (id)init {
if (self = [super init]) {
// custom initialization
}
return self;
}
In this case, the super keyword is used to call this class' superclass implementation of the -init method. super can be used on any method that your superclass implements.
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.
In a subclass, I'm overriding a method that is not exposed in the super class. I know that I have the correct signature as it is successfully overriding the superclass implementation. However, as part of the the new implementation, I need to call the superclass's implementation from the subclass's implementation.
Because it's not exposed I have to invoke the method via a call to performSelector:
SEL superClassSelector = NSSelectorFromString(#"methodToInvoke");
[super performSelector:superClassSelector];
However, in my application this results in an infinite recursive loop where the subclass's implementation is invoked every time I try to invoke the superclass's implementation.
Any thoughts?
I realize this is an atypical situation but unfortunately there's no way to get around what I'm trying to do.
The way I've dealt with this is to re-declare your super class' interface in your subclass implementation file with the method you want to call from the subclass
#interface MySuperclass()
- (void)superMethodIWantToCall;
#end
#implementation MySubclass
- (void)whateverFunction {
//now call super method here
[super superMethodIWantToCall];
}
#end
I'm not sure if this is the best way to do things but it is simple and works for me!
This doesn't work because you're only sending performSelector:, not the selector you pass to that, to the superclass. performSelector: still looks up the method in the current class's method list. Thus, you end up with the same subclass implementation.
The simplest way to do this may be to just write in your own call to objc_msgSendSuper():
// Top level (this struct isn't exposed in the runtime header for some reason)
struct objc_super
{
id __unsafe_unretained reciever;
Class __unsafe_unretained superklass;
};
// In the subclass's method
struct objc_super sup = {self, [self superclass]};
objc_msgSendSuper(&sup, _cmd, other, args, go, here);
This can cause problems in the general case, as Rob Napier has pointed out below. I suggested this based on the assumption that the method has no return value.
One way to go is to create a category of your class in a separate file with the method you are trying to expose
#interface MyClass (ProtectedMethods)
- (void)myMethod;
#end
and on the .m
#implementation MyClass (ProtectedMethods)
- (void)myMethod {
}
#end
Then, import this category from your .m files, and you're good to go. It's not the prettiest thing, but it'll do the trick
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.
Is it possible to create a class with no init method so as to force all callers to create the object with a factory method instead?
So basically, you want to make sure that your class is never initialized using -init, right? You can't do exactly what you want to do, but you can come close.
Since you inherit from NSObject, you have an init method and there's nothing you can do to prevent it from being called. That said, you could override init to this:
- (id)init
{
[self dealloc];
#throw [NSException exceptionWithName:#"MyExceptionName" reason:#"Reason" userInfo:nil];
return nil;
}
This way, anytime someone calls your -init method, it kills the object, so practically speaking, your init method is pretty much un-callable.
If you really wanted to cause trouble for users of your class who use init, you can do:
#implementation MyClass
- (id) init
{
// Still have to make sure the runtime has initialised everything for "self"
self = [super init];
if (!self) return nil;
[self release]; // some say you should use [super dealloc]
[super doesNotRecognizeSelector:_cmd];
return nil;
}
#end
You invoke super's doesNotRecognizeSelector: because you might want to implement your own behaviour for unrecognised selectors for your class.
Depends. If you have your class inherit from NSObject, it will have the inherited init method (which does nothing to your instance variables). So in that sense, even if you really really wanted to not have an init method, you'd most likely still have one. So if your question was "Do I need to implement a trivial init method?", the answer is "no, you don't need to". However, if your question was "Do I need to call the init method if I didn't override it?", then the answer is "yes, you do". Whatever you do with NSObject subclasses, at some point you still need to call init after the object is created. Such is the way of life.
That being said, you most likely want an init method, unless your object initialization requires nothing more than zeroing your whole object.
Otherwise, if you choose to not inherit from NSObject or any of its subclasses and just inherit from nothing, which is clearly a bad idea because of how the NSObject class deals with everything the ObjC runtime needs to do and the requirements are quite high, then you'll potentially end up with no init method at all. But seriously, don't try this at home.
Sure. In Objective-C, there are no actual constructors. init-type methods are typically used to initialize a class, in the same vein as a constructor, but they're just a "normal" method (there's nothing special about them like there are with, e.g., Java constructors).
That said, unless your class does no initialization for its instances, you probably want to have some sort of init method.
NSObject implements an init method for you that does whatever it does. If your class has nothing to setup when it's instantiated then simply do not override the -(id)init method provided by NSObject. But you still call it when you create the instance.