What does init in Objective-C actually do? - ios

+ (id)alloc;
and
- (id)init;
are methods from NSObject.h
The alloc does
+ (id)alloc {
return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
#if 0 && __OBJC2__
// Skip over the +allocWithZone: call if the class doesn't override it.
// fixme not - this breaks ObjectAlloc
if (! ((class_t *)cls)->isa->hasCustomAWZ()) {
return class_createInstance(cls, 0);
}
#endif
return [cls allocWithZone: nil];
}
It does memory allocation, and return a class Instance.
But when I came to the init method, this is the implementation
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
It only return self object (NSObject) without doing any initialization.
The documentation also says the same thing.
"The init method defined in the NSObject class does no initialization; it simply returns self."
If that is the case, alloc method alone is sufficient.
Init method is only required for overridding.
Any explanation here?
This is the implementation source NSObject.mm
http://www.opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm

alloc is to do with memory allocation while init (or initX etc., the init family) is to do with configuring that allocated memory as needed when an object is created - whether any particular class, now or in the future following some revision, needs to do any work in init is dependent on the semantics of that class. However as you don't know for any arbitrary class whether it's init needs to do any work you must call it, and as any arbitrary class does not know whether its superclass needs to do any initialisation to must call its superclasses init within its own init. For this chain to work NSObject must have an init, it so happens that it (currently, who knows in the future) does no work. NSObject's init is the end of the chain and the only one that does not need to call another init.
Confusion arises for some as many languages combine the two operations, allocation and initialisation, into one indivisible operation, e.g. new in Java. Indeed Cocoa has a new as well which is defined as alloc followed by init.
And Apple should really have written:
The init method defined in the NSObject class currently does no initialization; it simply returns self.
or simply said nothing.
HTH

CRD explained it pretty well.
I will state it more strongly, however.
The way you create a new object in Objective C is to use alloc/init.
You should always initialize every object, and custom init methods should always call [super init]. Consider failure to call init on ALL objects an error, and also consider calling [super init] in your custom init methods an error.

Related

How to use NS_DESIGNATED_INITIALIZER and override init?

Okay, this is admittedly a bit of a best practice question, but I want to get it right, so hopefully someone can enlighten me:
Scenario is pretty standard, but has one twist:
I have a class in a framework I write that inherits directly from NSObject. It has a designated initializer with quite a few arguments, most of which are nonnull. Since the thing is part of a framework, I explicitly use the NS_DESIGNATED_INITIALIZER macro (which I don't always do in smaller, personal apps).
The problem is that this leads to XCode warning me to also override init, i.e. the superclass's designated initializer. But additionally it demands that I call my designated initalizer from it, which I can't do, because I simply lack meaningful defaults for its arguments.
I don't really want to throw an exception in the "small" init, I'd much rather like to return nil.
To get rid of the warning, I added init as a second designated initalizer in my class's extension like so:
#interface MyClassName ()
// some other stuff not relevant`
-(nullable instancetype)init NS_DESIGNATED_INITIALIZER;
#end
Now I can safely just return nil; in the overriden init method like I wanted.
That means my documentation (I'm using appledoc) and by extension XCode's code completion won't tell someone using my framework that init is actually also a designated initializer (so they won't accidentally use it), but it is still there (in unit tests, for example, this could come in handy).
My question is: Are there any dangers to this besides from someone actually using it in production, happily sending messages to nil afterwards without realizing? Would this be one of the few cases where throwing an exception in init would be preferable?
Rather than just returning nil from init (and maybe adding a comment saying you shouldn't call it) – you should mark it as unavailable.
Not only will this dismiss the warning about you not overriding NSObject's designated initialiser – it will also generate a compile-time error if anyone tries to call init instead of your designated initialiser.
To do this, you can either use the NS_UNAVAILABLE macro, or use the unavailable __attribute__ as shown by this answer. The advantage of using the __attribute__ is you can specify a message that the compiler will present to the user.
For example:
#interface Foo : NSObject
-(instancetype) init __attribute__((unavailable("You cannot create a foo instance through init - please use initWithBar:")));
-(instancetype) initWithBar:(Bar*)bar NS_DESIGNATED_INITIALIZER;
#end
...
Foo* fooA = [[Foo alloc] init]; // ERROR: 'init' is unavailable: You cannot create a foo instance through init - please use initWithBar:
Foo* fooB = [[Foo alloc] initWithBar:[[Bar alloc] init]]; // No error

Why init.. methods are required in Objective C?

Wherever I've read, it's written to never use an object without calling it's initializer function. And initializer functions always "have to" start with "init".
Is there a reason behind this naming (Does naming a method starting with init do something special)? What happens if I do not call the initializer function i.e. if I just do [MyClass alloc] and start using the object?
Alloc is called to allocate space in memory for the data type you are specifying. Whether it is NSString or NSNumber, calling Alloc will reserve the most efficient space in memory as possible for that data type (small or large).
Init is called to instantiate the class and superclass's important variables. These variables could include the Rect to recalculate a certain size in order to layout subviews, or perhaps instantiate with a delegate of some kind to perform some protocol upon creation. If it all becomes too much for you Objective-C does allow you to instantiate objects like MyClass *myObject = [MyClass new];
It may all seem redundant and a waste of time, but fortunately Swift has cut down tremendous amounts of redundancies like this in the new programming language. Now all you have to do is var myObject = MyClass() and if there are any custom initializers they would likely be writen like so var myObject = MyClass(frame: CGRectZero)
Happy coding!
I recommend reading the Apple Documentation on Object Initialization, and Initialization.
Is there a reason behind this naming (Does naming a method starting with init do something special)?
It is the convention, beginning a method name with init does not do anything special.
There are some minor quirks, such as if a method beginning with init returns type id, the compiler will convert the return type to instancetype, but these are barely worth mentioning.
What happens if I do not call the initializer function i.e. if I just do [MyClass alloc] and start using the object?
If you're using a standard SDK class, you'll likely run into a crash / exception. Initialization is used to set up the initial state of the instance variables, and without doing this can lead to undefined behaviour.
For a small example, if you called [[MyObject alloc] vegetable]; with the following class, you'd get nil returned because _vegetable hasn't been assigned.
#interface MyObject : NSObject
-(NSString*)vegetable;
#end
#implementation MyObject {
NSString *_vegetable;
}
-(instancetype)init {
self = [super init];
if (self) {
_vegetable = #"Cabbage";
}
return self;
}
-(NSString*)vegetable {
return _vegetable;
}
#end
It is the way you create objects in Objective-C. It is a hard requirement of the language. Creating an object in Objective-C is a 2 step process: alloc and init.
Under the covers, the reason you must call init is, wait for it, initialization.
The call to alloc creates a block of memory for your object and zeros it out. Calling init allows the object and it's ancestors to set things up so the object is ready to function. It initializes the object's memory and does other housekeeping that set the object up.
Further, every object's init method needs to call super init, so the initialization goes all the way up the object chain, all the way to NSObject. All the ancestor classes are designed to assume that their init method is called.

Get super class instance in Objective C

How can I get super class instance?
id superObject=super;
Xcode says, "Use of undeclared identifier super"
It's not completely clear to me what you are trying to do, but based partly on your comments to your own question here is a guess.
It seems you may be confused over super in both Objective-C and Java. Both these languages are based on inheritance where a subclass instance is also an instance of its superclass. In neither language is there the concept of a "superclass instance/object". The meaning of super in the two languages is essentially the same, but differs in detail due to the different way the two languages support hiding.
In Objective-C super is a keyword while self, which references the instance a method was called on, is a variable (and the equivalent of Java's this). The super keyword is used to call a method on self but to start the search for the implementation to call in the superclass instead of the current class. It is usually used by an overriding method to call the method it has overridden.
However in your comments you also write:
I want to add a target with a method implemented in super class
which suggests you are trying to use an API which uses the target/action pattern, e.g. NSControl and others.
If the method you wish to call is not overridden in the current class then simply passing self as the target will invoke the superclass method. For example:
#interface Base : NSObject
- (IBAction) actionOne:(id) sender;
- (IBAction) actionTwo:(id) sender;
#end
#interface Child : Base
...
#end
#implementation Child
- (void) setActionFor:(NSControl *)aControl
{
aControl.target = self;
aControl.action = #selector(actionOne:); // actionOne is NOT overridden in Child
}
...
However if your subclass overrides a superclass method and you wish to set the superclass implementation as the action then you must write a method in your subclass which invokes the superclass method. For example, expanding on the previous example and passing Base's actionTwo as an action in a Child method when Child overrides actionTwo:
#implementation Child
// override Base method
- (IBAction) actionTwo:(id)sender { ... }
// provide a way to directly invoke Base method bypassing override
- (IBAction) superActionTwo:(id)sender
{
[super actionTwo:sender];
}
- (void) setActionFor:(NSControl *)aControl
{
aControl.target = self;
aControl.action = #selector(superActionTwo:); // indirectly set Base's actionTwo
}
HTH
Your self in-turn consist your super. You can access your super properties through self.
There isn't a separate object that represents your "super" object. Self is an instance of your class, which is part of a class hierarchy. When you call a method using the super keyword, you're specifying that you want to use your super class's implementation of that method. If your subclass doesn't override a method defined in super, then performing the selector on self will trigger a miss on your class's method lookup table and find the method definition on your superclass.
So, the simple answer is you should be able to accomplish what you're trying to do through self =]
Here you are trying initialize object with a class, not with instance of this class.
May be something like this?
id instance = [super init];

Prevent class from being allocated in iOS

I'm wondering how it is possible to create a class in Objective-C that cannot be allocated (or init)?
I'm trying to replicate the SKDownload Apple class (StoreKit framework) and I noticed that the documentation doesn't mention any alloc or init methods.
This may be missing in the documentation but present in the code ? (some .h declaration missing to prevent us from using this method?). I've tried to +alloc-init this class and either alloc and init return null.
What I'm trying to achieve is a class B that possesses only getters, that can only be created by a class A (just like factory methods would do - only A can create and return B instances, but the user cannot create B directly).
Example:
#interface A : NSObject
// Only A "+createB" class method can create B instances
+ (Second *)createBWithValue:(int)value;
#end
#interface B : NSObject
- (id)init; // return nil
+ (id)alloc; // return nil
- (int)value; // this returns the value passed from A
#end
Question 1: How would you proceed to create such classes? Is is possible ?
Question 2: How can - (int)value on B return the value passed in the class method in A? (knowing that +alloc, -init and/or other memory allocation methods may be nullified since users cannot create B classes directly - also custom initWithValue methods in B may be unavailable too)
I'm a little confused on how the Apple engineers do that..are there hidden methods that Apple doesn't want me to use?
Thanks you.
I noticed that the documentation doesn't mention any alloc or init methods
From the documentation:
Your app never directly creates a SKDownload object. Instead, after a payment is processed, your app reads the transaction object’s
downloads property to retrieve an array of SKDownload objects
associated with the transaction.
are there hidden methods that Apple doesn't want me to use?
Yes. There are a great many hidden methods that Apple doesn't want you to use.
So, the idea is that you're not supposed to create instances of SKDownload yourself, and for that reason the initialization method isn't part of the public API. That doesn't mean that there isn't a way to create SKDownload instances, just that there's not a public way for you to create them.
It's pretty common for classes to limit allocation or initialization. Singleton classes, for example, will often override +allocWithZone: such that it only allocates a new object if one hasn't already been created; if one exists, it returns that object instead (and the initializer basically does nothing).
Hide the initializer.
If you want to create a class like that yourself, override the inherited initialization method(s) (probably just -init) so that they return nil or maybe even throw an exception. Implement your own initializer, but keep it out of the public interface for the class:
// B.h
#interface B : NSObject
#property(readonly) int value;
#end;
// B.m
#interface B ()
#property(readwrite) int value;
-(id)secretInitializerWithValue:(int)v;
#end
#implementation B
-(id)secretInitializerWithValue:(int)v
{
if ((self == [super init])) { // not really necessary since NSObject's -init doesn't do anything
_value = v;
}
}
-(id)init
{
#throw [NSException exceptionWithName:#"Don't do that!"
reason:#"You shouldn't instantiate B yourself."
userInfo:nil];
}
#end
Private mutable subclass.
Another option is to create an immutable class with the read-only accessors you want, and a private mutable subclass that has read-write accessors. You can implement -copyWithZone: in the mutable subclass so that you get an immutable object when you make a copy. This all amounts to about the same thing I described above -- either way, you're hiding the creation mechanism from the client.

how to forbid the basic init method in a NSObject

I want to force user to use my own init method (for example -(id)initWithString:(NSString*)foo;) and not the basic [[myObject alloc]init];.
how can I do that?
All other answers here are outdated. There is a way to do this properly now!
While it is easy to just crash at runtime when somebody calls your method, compile-time checking would be far preferable.
Fortunately, this has been possible in Objective-C for a while.
Using LLVM, you can declare any method as unavailable in a class like so
- (void)aMethod __attribute__((unavailable("This method is not available")));
This will make the compiler complain when trying to call aMethod. Great!
Since - (id)init is just an ordinary method, you can prohibit calling of the default (or any other) initializer in this way.
Note, though, that this will not insure against the method being called using the dynamic aspects of the language, for instance via [object performSelector:#selector(aMethod)] etc. In the case of init, you won't even get a warning, because the init method is defined in other classes, and the compiler doesn't know enough to give you an undeclared selector warning.
So, to ensure against this, make sure that the init method crashes when being called (see Adam's answer).
If you want to disallow - (id)init in a framework, make sure to also disallow + (id)new, as this will just forward to init.
Javi Soto has written a small macro to forbid using the designated initializer faster and easier and to give nicer messages. You can find it here.
tl; dr
Swift:
private init() {}
Since all Swift classes include an internal init by default, you can change it to private to keep other classes from calling it.
Objective C:
Put this in your class's .h file.
- (instancetype)init NS_UNAVAILABLE;
This relies on an OS define that prevents the method named from being called.
The accepted answer is incorrect - you CAN do this, and it's very easy, you just have to be a bit explicit. Here's an example:
You have a class named "DontAllowInit" which you want to prevent people init'ing:
#implementation DontAllowInit
- (id)init
{
if( [self class] == [DontAllowInit class])
{
NSAssert(false, #"You cannot init this class directly. Instead, use a subclass e.g. AcceptableSubclass");
self = nil; // as per #uranusjr's answer, should assign to self before returning
}
else
self = [super init];
return nil;
}
Explanation:
When you call [super init], the class that was alloc'd was the SUBCLASS.
"self" is the instance - i.e. the thing that was init'd
"[self class]" is the class that was instantiated - which will be SUBCLASS when the SUBCLASS is calling [super init], or will be the SUPERCLASS when the SUPERCLASS is being called with plain [[SuperClass alloc] init]
So, when the superclass receives an "init" call, it just needs to check whether the alloc'd class is the same as its own class
Works perfectly. NB: I don't recommend this technique for "normal apps" because usually you INSTEAD want to use a Protocol.
HOWEVER ... when writing Libraries ... this technique is VERY valuable: you frequently want to "save (other developers) from themselves", and its easy to NSAssert and tell them "Oops! you tried to alloc/init the wrong class! Try class X instead...".
-(id) init
{
#throw [NSException exceptionWithName: #"MyExceptionName"
reason: #"-init is not allowed, use -initWithString: instead"
userInfo: nil];
}
-(id) initWithString: (NSString*) foo
{
self = [super init]; // OK because it calls NSObject's init, not yours
// etc
Throwing the exception is justified if you document that -init is not allowed and therefore using it is a programmer error. However, a better answer would be to make -init invoke -initWtihString: with some suitable default value i.e.
-(id) init
{
return [self initWithString: #""];
}
Short answer: you can't.
Longer answer: the best practice is to set your most detailed initializer as the designated initializer, as described here. 'init' will then call that initializer with sane, default values.
Another option is to 'assert(0)' or crash in another way inside the 'init', but this isn't a good solution.
I actually voted up Adam's answer, but would like to add some things to it.
First, it is strongly encouraged (as seem in auto-generated init methods in NSObject subclasses) that you check self against nil in inits. Also, I don't think class objects are guaranteed to be "equal" as in ==. I do this more like
- (id)init
{
NSAssert(NO, #"You are doing it wrong.");
self = [super init];
if ([self isKindOfClass:[InitNotAllowedClass class]])
self = nil;
return self;
}
Note that I use isKindOfClass: instead because IMHO if this class disallows init, it should disallow its descendants to have it as well. If one of its subclass want it back (which doesn't make sense for me), it should override it explicitly by calling my designated initializer.
But more importantly, whether you take the above approach or not, you should always have appropriate documentation. You should always clearly state which method is your designated initializer, try as best as you can to remind others not to use inappropriate initializers in documentation, and put some faith in other users/developers, instead of trying to "save everybody else's asses" with clever codes.

Resources