Is testing for self still necessary and/or meaningful? [duplicate] - objective-c

This question already has answers here:
In Objective-C why should I check if self = [super init] is not nil?
(9 answers)
Closed 9 years ago.
A usual initialization sequence in an Objective-C instance (e.g. in the designated initializer) goes like:
- (id)initWithFrame: (NSRect)frame {
self = [super initWithFrame: frame];
if (self != nil) {
// Do your stuff.
}
return self;
}
This is a well known pattern, but is it really necessary to test if self is assigned? I mean, if something fails in the super method, wouldn't it rather raise an exception than just returning nil? Is it a safe pattern at all? What if the call to super had a problem but still returns a (random) pointer?
It's certainly a matter of defensive programming but it really looks exaggerating to have so many many tests for cases you know for sure that there's never a nil result. It makes of course a lot of sense to check self if you know that it can occur (which must be documented then).

Raising an exception is not reasonable behavior in an objective-c program when something goes wrong. Throwing exception should only used for critical, unrecoverable, programmer-error situations.
The correct way to signal error from an init method is to return nil and optionally fill in a passed NSError-pointer with more information. Testing if your super initializer returns nil prevents you from doing useless initialization, and helps avoid possible crashes like:
#implementation {
int _foo; // instance variable
}
- (id)initWithFrame: (NSRect)frame {
self = [super initWithFrame: frame];
if (self != nil) {
_foo = 2;
}
return self;
}
This will crash if the super initializer returns nil, because _foo = 2 is short-hand for self->_foo = 2, which would dereference a nil pointer. (Only message sends return nil when self is nil; accessing an ivar directly doesn't make that guarantee.)

Some classes, even Apple's own NS classes can return nil from an initializer, e.g. NSArray's and NSDictionary's initWithContentsOfFile: and initWithContentsOfURL: methods. Should those throw an exception instead? NO! They're easily detectable and easily recoverable cases. Throwing an exception would be nothing but a crutch for lazy programmers who can't be bothered to put in proper error-checking and a bother for those of us who do.

The question boils down to: Are there any (many) known classes that do return nil from an initializer?
I think there are few. Of course there are known cases (where the constructor often takes an NSError by reference) that have to return nil to point out an error.
But most other initializers either return the self argument or another instance of the class. If that's not the case it should be clearly expressed in the documentation, as code usually relies on object creation not failing.
So I think it's totally safe to omit the test for nil when you checked the superclass's behavior.
On the other hand the pattern is so omnipresent that co-developers might be confused about the omission and might point it out as an oversight. So if you plan to switch to a style without the condition it should at least be somehow communicated to your fellow developers.

Related

Instance variable initialized without using "init" method?

Iam in the first phase of Objective-C learning curve, so please bear with me :).
Iam reading Apple documentation and doing exercises there also. Ive come to a problem with initialization of particular object, because I get unexpected (In my opinion) behavior in my Xcode.
To the point (lets make it simple):
STEP 1:
I have declared simple variable from a my custom class named XYZPerson. It looks like this:
XYZPerson *randomPerson;
"If" check for initialized object returned out "Not Initialized" (as expected).
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
STEP 2:
I have allocated the memory for this object using "alloc" word on this variable. As I understand in this phase, memory for this variable gets allocated. It also returns the pointer?
Line of code looks like this:
XYZPerson *randomPerson = [XYZPerson alloc];
Checking for "nil" surprised me: (It executes "else" statement).
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
I can also send messages to this object and they are executed just fine, without calling "init" method.
How can this be? Am I missing something? How can "randomPerson" variable be initialized before calling "init" method on this instance? Why is "init" there in the first place then?
Furthermore, the accepted answer here Difference between ! and nil check on Objective-C object , says that ALL object are set to nil in the alloc method!
Iam coming from Java and there is one way of declaring an object using "new" keyword. Without using "new" the instance of object will be always "null", and calling methods on that instance will result "NULLPOINTEREXPECTION Error".
P.S Is "nil" in objective C, same as "NULL" in Java?
Whole project looks like this:
main method:
XYZPerson *randomPerson = [XYZPerson alloc];
if(randomPerson == nil) {
NSLog(#"Random person is not initialized");
} else {
NSLog(#"Random person is initialized");
}
**XYZ Person Class:**
#import "XYZPerson.h"
#implementation XYZPerson
-(void)sayHello {
//[self saySomething];
}
-(void)saySomething:(NSString *) greeting {
NSLog(#"%#", greeting);
}
#end
I post an answer additional to that one linked in the comment for two reasons:
A. Your Q is slightly different.
B. I do not confirm with the linked answer in details.
First of all to your additional Qs: Yes, nil is Objectice-C's NULL. But there are some differences:
A. In most cases (using ARC) a reference to a pointer (not the object itself) is initialized with nil.
XYZPerson *person; // Usually automatically set to nil.
B. A message to nil is allowed in Objective-C, does not throw an exception and returns NO, 0, 0.0, nil, or whatever the representation of null resp. zero (if it does not have a null representation like integers) is for that type. Sometimes this is done intentionally and you can rely on that.
Two your main Q:
After creating an instance object with +alloc all instance variable (ivars) are set to NO, 0, 0.0, nil, or whatever the representation of null resp. zero is for that type. You should not set such ivars explicitly to that value.
For example, if the instances of the class XYZPerson has an ivar for the name typed NSString*, the ivar will be nil. So, one might think that an naked -init does not have any meaning, because it does not take parameters and therefore does nothing. But you simply do not know: Maybe something else is done in -init. And, that's probably a surprise for a Java developer, -init returns an object reference, so you cannot know, whether -init replaces the instance object, for example for twin toning. So even you do not see any meaning in it, the first message to an instance object has to be init. (Yes, in many case you would not see any difference, but you do not know, whether there is one or not or will be in future. It is a part of the API contract, so do it.)
In disagree with the linked answer in one point:
Sending +new… instead of +alloc -init is the better way to do it.
A. If you use a simple +new it is correct that it sends +alloc -init in many cases. Therefore it is obvious that this is not better or worse than sending +alloc -init. It is what it does. You always have to have a look at the documentation, whether a naked initialization, using +new or +alloc -init is allowed. But in such a case you likely do not want to do a naked initialization, because it is meaningless.
B. Sometimes it is for the implementor of a class easier to receive a new message to the class object.

Objective C class initialization

+ (id)packetWithType:(PacketType)packetType
{
return [[[self class] alloc] initWithType:packetType];
}
- (id)initWithType:(PacketType)packetType
{
if ((self = [super init]))
{
// code
}
return self;
}
Why do we need first class method, isn't second one just enough for initialization ??
There are two reasons for having convenience constructor class methods. The first one is, that the idiom of [[Thing alloc] initWithFoo: xyz] is really common but inconvenient to have to type anywhere. So, [Thing thingWithFoo: xzy] is a common abbreviation.
A deeper reason has to do with reference counting. Methods starting with init are supposed to return a reference of the instance, ownership of which is transferred to the caller. Wheras the convenience class methods usually return autoreleased references:
+ (id)packetWithType:(PacketType)packetType
{
return [[[[self class] alloc] initWithType:packetType] autorelease];
}
This is important to know in order to avoid dangling references and/or memory leaks:
Thing* thing = [[Thing alloc] initWithFoo: xyz];
// Now, *I* own the reference and *I* am responsible for releasing
// it, when I no longer need it.
[thing release]
On the other hand, the reference returned by
Thing* thing = [Thing thingWithFoo: xyz];
is owned by the "nearest" NSAutoreleasePool. The caller is not responsible for releasing it (in fact, that would be wrong!). If the reference is to be kept around, the caller must actually retain it here:
self->myMember = [thing retain];
You should know about these conventions even when using ARC, as the underlying rules are still in effect, even if (under ARC) it's the compiler, who generates the code to obey them. The NARC acronym is a nice way to remember, which method name prefixes come with certain responsibilities. This answer has the details.
Convenience constructors have their place in the language for some reasons. Of course using them is usually shorter but there are other advantages as well:
The object is not yet allocated when they are called so the method can decide which class to allocate. Class clusters might use this to find the proper class depending on the parameters of the constructor.
The method might also decide to return an already existing object from a shared cache.
The return value can be statically typed.
Note that your convenience constructor would typically be:
+ (Packet *)packetWithType:(PacketType)packetType
{
return [[self alloc] initWithType:packetType];
}
Now the return type is statically typed and we don't send the (redundant) class message to the class object. With recent compiler versions one could use instancetype as the return type.

Using objc_msgSendSuper to invoke a class method

I was going through and replacing #synthesized(self) locks w/ this method
void _ThreadsafeInit(Class theClassToInit, void *volatile *theVariableItLivesIn, void(^InitBlock)(void))
{
//this is what super does :X
struct objc_super mySuper = {
.receiver = (id)theClassToInit,
.super_class = class_getSuperclass(theClassToInit)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL, NSZone *) = (void *)&objc_msgSendSuper;
// id (*objc_superAllocTyped)(id objc_super, SEL, NSZone *) = (void *)&objc_msgSend;
do {
id temp = [(*objc_superAllocTyped)(&mySuper /*theClassToInit*/, #selector(allocWithZone:), NULL) init];//get superclass in case alloc is blocked in this class;
if(OSAtomicCompareAndSwapPtrBarrier(0x0, temp, theVariableItLivesIn)) { //atomic operation forces synchronization
if( InitBlock != NULL ) {
InitBlock(); //only the thread that succesfully set sharedInstance pointer gets here
}
break;
}
else
{
[temp release]; //any thread that fails to set sharedInstance needs to clean up after itself
}
} while (*theVariableItLivesIn == NULL);
}
which while a bit more verbose exhibits significantly better performance in non-contested cases
along with this little macro (excuse poor formatting, it's very simple). To allow the block to be declared after the initial nil check, looks to help LLVM keep the "already initialized" path extremely fast. That's the only one I care about.
#define ThreadsafeFastInit(theClassToInit, theVariableToStoreItIn, aVoidBlockToRunAfterInit) if( theVariableToStoreItIn == nil) { _ThreadsafeInitWithBlock(theClassToInit, (void *)&theVariableToStoreItIn, aVoidBlockToRunAfterInit); }
So initially implemented it using the commented out sections for objc_superAllocTyped (actually first using [theClassToInit allocWithZone:NULL], which was definitely the best approach :) ), which worked great until I realized that most of the singletons in the project had overridden allocWithZone to return the singleton method... infinite loop. So I figured using objc_msgSendSuper should sort it out quickly, but I get this error.
[51431:17c03] +[DataUtils allocWithZone:]: unrecognized selector sent to class 0x4f9584
The error doesn't seem to be related to the actual problem, as...
(lldb) po 0x4f9584
$1 = 5215620 DataUtils
(lldb) print (BOOL)[$1 respondsToSelector:#selector(allocWithZone:)]
(BOOL) $2 = YES
So I'm definitely missing something... I compared to assembly generated by a [super allocWithZone:NULL] method in an empty class... almost identical except for the functions called have different names (maybe just using different symbols, no idea, can't read it that well).
Any ideas? I can use class_getClassMethod on the superclass and call the IMP directly, but I'm trying to be reasonable in my abuse of the runtime :)
Alright, this wasn't actually that tricky once I recalled that the meta class contains all of the method information for a Class instance obtained via -[self class] or +[self] -> thanks http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html
This error occurred because I was asking the runtime to look up the method in NSObject's set of instance methods, which obviously doesn't contain allocWithZone: . The mistake in the error log presumably originated because the receiver was a metaclass instance, and Apple has their interns implement error logs.
so while with a normal instance method call via objc_msgSendSuper, you would pass a metaclass instance as objc_super.super_class, to invoke a class method, the metaclass itself is needed (everything is one level up).
Example, and a diagram that helped me understand this - (http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html)
struct objc_super mySuper;
mySuper.receiver = theClassToInit; //this is our receiver, no doubt about it
//either grab the super class and get its metaclass
mySuper.super_class = object_getClass( class_getSuperclass( theClassToInit ) );
//or grab the metaclass, and get its super class, this is the exact same object
mySuper.super_class = class_getSuperclass( object_getClass( theClassToInit ) );
Then the message can be resolved correctly. Makes perfect sense now that I started paying attention :P
Anyways, now that I found my mistake I feel like I've leveled up my Objc runtime understanding. I was also able to fix an architectural mistake made two years ago by someone I never met without having to modifying and re-test dozens of classes across 3 projects and 2 static libraries (God I love Objective-C). Replacing the #synchronized construct with a simple function call also halved the compiled code size of those methods. As a bonus, all our singleton accessors are now (more) threadsafe, because the performance cost for doing so is now negligible. Methods which naively re-fetched the singleton object multiple times (or in loops) have seen a huge speedup now that they don't have to acquire and release a mutex multiple times per invocation. All in all I'm very happy it all worked as I'd hoped.
I made a "normal" Objective-C method for this on a category of NSObject, which will work for both instance and Class objects to allow you to invoke a superclass's implementation of a message externally. Warning: This is only for fun, or unit tests, or swizzled methods, or maybe a really cool game.
#implementation NSObject (Convenience)
-(id)performSelector:(SEL)selector asClass:(Class)class
{
struct objc_super mySuper = {
.receiver = self,
.super_class = class_isMetaClass(object_getClass(self)) //check if we are an instance or Class
? object_getClass(class) //if we are a Class, we need to send our metaclass (our Class's Class)
: class //if we are an instance, we need to send our Class (which we already have)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper; //cast our pointer so the compiler can sort out the ABI
return (*objc_superAllocTyped)(&mySuper, selector);
}
so
[self performSelector:#selector(dealloc) asClass:[self superclass]];
would be equivalent to
[super dealloc];
Carry on runtime explorers! Don't let the naysayers drag you into their land of handwaving and black magik boxes, it's hard to make uncompromisingly awesome programs there*.
*Please enjoy the Objective-C runtime responsibly. Consult with your QA team for any bugs lasting more than four hours.

Assigning to self in Objective-C

I'm from the C++ world so the notion of assigning this makes me shudder:
this = new Object; // Gah!
But in Objective-C there is a similar keyword, self, for which this is perfectly acceptable:
self = [super init]; // wait, what?
A lot of sample Objective-C code uses the above line in init routines. My questions:
1) Why does assignment to self make sense (answers like "because the language allows it" don't count)
2) What happens if I don't assign self in my init routine? Am I putting my instance in some kind of jeopardy?
3) When the following if statement fails, what does it mean and what should I do to recover from it:
- (id) init
{
self = [super init];
if (self)
{
self.my_foo = 42;
}
return self;
}
This is a topic that is frequently challenged by newcomers:
Wil Shipley: self = [stupid init];
Matt Gallagher: What does it mean when you assign [super init] to self?
Apple documentation: Implementing Initializers
Cocoa-Dev: self = [super init] debate
Basically, it stems from the idea that a superclass may have over-ridden the designated initializer to return a different object than the one returned from +alloc. If you didn't assign the return value of super's initializer into self, then you could potentially be dealing with a partially initialized object (because the object that super initialized isn't the same object that you're initializing).
On the whole, it's pretty rare for super to return something different, but it does happen in a couple of cases.
In Objective-C, initializers have the option of returning nil on failure or returning a completely different object than the one the initializer was called on (NSArray always does this, for example). If you don't capture the return value of init, the method might be executing in the context of a deallocated object.
Some people disagree about whether you should do the whole assign-to-self rigamarole if you don't expect to get something else back from the superclass initializer, but it's generally considered to be good defensive coding.
And yes, it looks weird.
It is true that init may return nil, if the initialization fails. But this is not the primary reason why you should assign to self when you implement your own initializers.
It has been mentioned before, but it is needed to stress even harder: the instance returned from an initializer may not be the same instance as the one you sent in, in fact it may not even be of the same class!
Some classes use this as a standard, for example all initializer to NSString and NSArray will always return a new instance of a different class. Initializers to UIColor will frequently return a different instance of a specialized class.
And you yourself can happely implement something like this if you want:
-(id)initWithName:(NSString*)name;
{
if ([name isEqualToString:#"Elvis"]) {
[self release];
self = [[TheKing alloc] init];
} else if (self = [super init]){
self.name = name;
}
return self;
}
This allows you to break out the implementation of some special case into a separate class, without requiring the clients of your API to care or even know about it.
All the other points here are valid, but it's important for you to understand as well that self is an implicit parameter to every Objective-C method (objc_msgSend() passes it) and can be written to, just like any other method parameter. (Writing to explicit parameters is generally frowned upon, unless they are out parameters.)
Typically, this is only done in the -init method, for the reasons others have stated. It only has any effect because self is returned from the method and used in the assignment id obj = [[NSObject alloc] init]; It also affects the implicit resolution of ivars, because, for example, if myVar is an ivar of my class, then accessing it in a method causes it to be implicitly resolved to self->myVar.
I'm still new to Objective C, but this post helped me in understanding this.
To sum it up, most init calls return the same object that self is already initialized to. If there is an error, then init will return nil. Also, some objects such as singletons or unique objects (like NSNumber 0) will return a different object than the one initialized (the singleton or a global 0 object). In these situations you need to have self reference that object. I'm by no means an expert in what is going on behind the scenes here, but it makes sense on the surface, to me.
If [super init] returns nil that means that you have been deallocated and your self parameter is now an invalid pointer. By blindly following the self = [super init] convention you will save you from potentially nasty bugs.
Consider the following non-typical initializer:
- (id)initWithParam:(id)param {
if (!param) {
// Bad param. Abort
self = [super init]; // What if [super init] returns nil?
[self release];
return nil;
}
else
{
// initialize with param.
...
}
}
Now what happens if my superclass decides to abort and return nil? I have been de-allocated and my self parameter is now invalid and [self release] will crash. By re-assigning self, I avoid that crash.

[self release], [self dealloc] or [super dealloc] in init methods?

I've just been reading up on how to properly fail in an init method and the docs seem to disagree with each other. One recommends throwing an exception while the others recommend cleaning up and returning nil. What's the current best practice here?
I believe that the generally accepted practice is to return nil on failure. But you do want to release self to avoid a leak:
-(id)init
{
if (self = [super init]) {
...
if (thingsWentWrong) {
[self release];
return nil;
}
...
}
return self;
}
The correct solutions (exceptions and/or [self release]; return nil;) having been covered, I'll address the incorrect solutions.
Don't send dealloc directly. That's release's job. (And if your code is ever running under GC, dealloc is inapplicable, and I could only speculate on what problems calling it would cause.)
Double-don't use super to send it directly. That would skip over your own dealloc implementation.
Cocoa's philosophy on exceptions is that they should only be thrown in situations that are programmer errors, like passing an illegal argument to a method. If something else goes wrong, the method should just return NO or nil, and hopefully report the details via an NSError** "out" parameter.
This includes -init methods. If the error situation is something that could legitimately occur in the finished product, then the method should release self (to avoid a leak) and return nil.
The method I've always used is cleaning up and returning nil. The three methods you mention in your question title may cause segfaults higher up in the call hierarchy, whereas returning nil will not. I believe that the Apple docs themselves say to return nil on failure. Where are you finding discrepancies?