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.
Related
The following is commonplace in Objective-C.
- (id)init {
if (self = [super init]) {
// custom initialization
}
return self;
}
Because -init is an instance method, it must be called on a valid instance of a class, and an instance of a class must be instantiated. This instantiation uses up memory. Does calling -init on super use memory as it has to instantiate a new object to call the init method on?
Does calling -init on super use memory as it has to instantiate a new object to call the init method on?
No, because your premise is false. super, as the receiver of a message, is the same object as self; it's just a signal to the compiler to use objc_msgSendSuper() instead of objc_msgSend(). The former starts method lookup with the superclass, but the instance that runs the method is still the same.
No new instance is created by using the super keyword.
It all depends on what's going on up the chain.
If it's simply "[NSObject init]", then nothing (obvious) is happening.
But if it's "[BrianTracySuperAwesomeBaseClass init]" with a lot of initialization and setting up various ivars and properties, then yes, you're using a bit of memory.
In general, you shouldn't load down your "init" methods with lots of stuff going on... instead you should rely on lazy loading or allocate-on-demand things for your Objective C objects.
In iOS Programming Book from Big Nerd Ranch (3rd ed) they say on pg.194
..a knowledgeable programmer could still create an instance of BNRItemStore via allocWithZone:, which would bypass our sneaky alloc trap.To prevent this possibility, override allocWithZone: in BNRItemStore.m to return the single BNRItemStore instance.
+(id) allocWithZone:(NSZone *)zone
{
return [self sharedStore];
}
This statement seems confusing to me. Doesn't this following code not prove this wrong in a way-
#import <Foundation/Foundation.h>
#interface BNRItemStore : NSObject
+(BNRItemStore *)sharedStore;
+(id)retrieveObject;
#end
#implementation BNRItemStore
+(BNRItemStore *)sharedStore{
static BNRItemStore *sharedStore=nil;
if (!sharedStore){
NSLog(#"Test2");
sharedStore= [[super allocWithZone:nil] init];
}
NSLog(#"sharedStore-> %#",sharedStore);
return sharedStore;
}
+(id)allocWithZone:(NSZone *)zone{
NSLog(#"Test1");
return [self sharedStore];
}
+(id)alloc{
NSLog(#"Retrieving super object");
NSLog(#"%#", [super allocWithZone:nil]);//Bypassing the subclass version of allocWithZone.
return [super allocWithZone:nil];
}
#end
int main(){
[[BNRItemStore alloc] init]; //the alloc message triggers a call to the subclass (overriding) version of +(id)alloc method
}
The output is:
2013-10-18 18:24:40.132 BNRItemStore[381:707] Retrieving super object
2013-10-18 18:24:40.134 BNRItemStore[381:707] BNRItemStore:0x7f8c72c091e0
If the call [super allocWithZone:nil] inside of subclass 'alloc' method would have triggered a call to subclass allocWithZone,the console would be logging "Test1" and "Test2" and finally would lead to static pointer getting allocated. But this did not happen.
This means that if we directly call [NSObject allocWithZone:nil] or [super allocWithZone:nil], the message would not redirect to the overriding version (subclass version) of allocWithZone but will give direct access to NSAllocateObject() function which does the actual allocation.
The code of +(id)allocWithZone in NSObject must look somewhat like this-
+(id)allocWithZone:(NSZone *)zone{
return NSAllocateObject();
}
Had this implementation(NSObject's allocWithZone:) included something like [self allocWithZone], the message dispatch mechanism would have included the subclass version of allocWithZone which would then make us go through the "sneaky" trap involving a call to sharedStore method.Following is the case that I'm talking about. Now if this were the case the code would definitely have infinite-looped.Clearly this isn't the case.
+(id)allocWithZone:(NSZone *)zone{
if([self allocWithZone:zone]) //this would trigger a call to subclass ver. which would call sharedStore method which would then have [super allocWithZone:nil].Infinite Loop
return NSAllocateObject();
}
So can someone clear up this query about this so called "sneaky" trap. Was the trap meant for blocking anyone from instantiating separately .i.e not being able to use NSObject's allocWithZone except when inside of sharedStore method ? Pls clarify..
The first, most important lesson here is that you should not override +allocWithZone:. I know the BNR book describes it (and the BNR book is generally very good). You shouldn't do it. I know that Apple includes some example code that does it. You shouldn't do it. (And Apple notes in the explanation that it is rare to need this.) Singletons should be created with the dispatch_once pattern.
You don't give the initial code, but I suspect that their example code overrides alloc, but not allocWithZone:. They're simply saying that if the caller uses allocWithZone:, it won't go through alloc, so they've also overridden alloc to catch that. (Of course the right answer would be just to override allocWithZone: and not alloc. But you shouldn't be overriding these methods in any case.)
EDIT:
I believe you are misunderstanding what "our sneaky alloc trap" means here. The author is assuming the following code at this point in the text:
#interface BNRItemStore : NSObject
+(BNRItemStore *)sharedStore;
#end
#implementation BNRItemStore
+(BNRItemStore *)sharedStore{
static BNRItemStore *sharedStore=nil;
if (!sharedStore){
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
#end
That's it; no +alloc overrides at all. It then points out "to enforce the singleton status…you must ensure that another instance of BNRItemStore cannot be allocated." (*)
The author goes on to suggest that we might enforce the singleton status by overriding +alloc, but immediately notes that this is insufficient, since the caller can use +allocWithZone: instead. Since it is documented that [NSObject alloc] calls [self allocWithZone:], it is necessary and sufficient to override +allocWithZone: and unnecessary and insufficient to override +alloc.
What you've done in your code is demonstrate that you can modify BNRItemStore to call [super allocWithZone:] in +alloc. That is not the point. If you can modify BNRItemStore, you could also make it a non-singleton. The point is whether an outside caller (main() in your case) can bypass the singleton instantiation, which she cannot. (**)
(*) The point it doesn't make at this point, and probably should, is that it is generally a bad idea to "enforce the singleton status" by quietly returning a singleton when the callers asked you to allocate a new object. If you need to enforce the singleton status, it is better IMO to do so with an assertion in init, since the request for a second allocation represents a programming error. That said, there are times when "transparent" singletons of immutable objects can be useful for performance reasons, such as the special singletons NSNumber provides for certain common integers, and this technique is appropriate in those cases. (By "transparent," I mean that the singleton-ness is an implementation detail that the caller should never worry about. This presumes at a minimum that the object is immutable.)
(**) Actually she can if she is determined to do so. She could always call NSAllocateObject() herself, bypassing +alloc entirely, and then call -init. This would of course be insane, and there is no reason to "protect" her from herself in doing this. It is not the job of an SDK to protect itself from the caller. It is only the job of an SDK to protect a caller from likely mistakes. The caller is never the enemy.
i'm not sure if this quite answers your question or not, but "allocWithZone:" was used back in the day to be able to partition the memory allocated. apple has since moved away from this concept and expects everything to be allocated in the same heap space. "allocWithZone:" does not even function the way it used to, and apple specifically says not to use it.
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.
Say I have a pseudo-abstract base class that users should not instantiate. Basically I want to throw a warning when they're trying to call init on the class, or return one of the concrete instances with default values.
However, the concrete implementations of that base class have to call [super init] in their initializers. That should of course be allowed.
How would I best go about this?
I was thinking that this should be fine:
#implementation KTPhysicsShape
-(id) init
{
// throw exception here or return concrete instance with default values
}
// this is what subclasses would call in place of [super init]:
-(id) internal_initFromSubclass
{
return [super init];
}
#end
Any concerns about this approach? I know others could still call the internal method, but I'm mostly concerned about disallowing init since that's what users would try to call foremost.
I have also worked at the problem of how to have effectively abstract classes, but I'm not that into this solution. It seems to me that it's going to make your subclass code look weird and harder to read for casual observers.
If you require that your subclasses do particular initialization in -init, yours may the only solution. But if you just want to ensure that they have subclassed, you can do that within -init:
-(id) init
{
NSAssert(![self isMemberOfClass:[KTPhysicsShape class]],
#"KTPhysicsShape must be subclassed!");
return [super init];
}
This indicates that your architecture has a serious flaw. The whole point of the designated initializer chain is that it can be executed in a predictable order without variation. Adding contractual obligations to the subclasses to not follow the normal chain adds fragility and unneeded complexity.
The crux of the flaw is that you have an abstract class that doesn't appear to be truly abstract; it can have concrete instances and that requires concrete initialization.
First, why can't you break the class into a truly abstract class and a concrete class?
If you can't (or don't want to -- certainly, more classes has costs of its own), then one solution is to break out the commonly used initialization operations into a separate method:
- (void) commonKTPhysicsShapeInit
{
....
}
That does not call super. This would not be declared in your header; it is an internal-to-implementation-only method, thus the name.
Then, let your subclasses call through the standard designated initializer that calls commonInit. For concrete instances of that class, have a separate initializer that both calls commonInit and does the concrete initialization dance.
It is similar to what you proposed, but presents the interface in a fashion that follows existing patterns more closely.
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).