What to do with useless init? - objective-c

This is currently what I have for my init,
- (id)init
{
self = [super init];
if (self) {
self.url = [[NSURL alloc] init];
self.blurb = [[NSString alloc] init];
self.author = [[NSString alloc] init];
}
return self;
}
It does nothing, but I have another method called initWithObject: that will use its argument to fill up the instance variables url, blurb, and author. I don't know what I should be doing with this init. Should I throw an exception? What other options do I have?

If you want to override your standard -init method you could either return nil (if you do not want -init to be used) or do:
- (instancetype)init
{
return [self initWithObject:nil];
}
If you want to stop the use of -init completely you can tag it as an unavailable attribute or use NSAssert:
// .h
// ...
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
// ...
// .m
- (instancetype)init
{
NSAssert(NO, #"This method should not be used");
return nil;
}
You can use either UNAVAILABLE_ATTRIBUTE or NSAssert(), but if you use UNAVAILABLE_ATTRIBUTE you need some kind of implementation of -init, even if it just returns nil.

You don't have to have a plain init—you can simply have your initWithObject: method. If you're doing any basic setup that will remain the same 80% of the time, or if you have some common code in all your initializers, you can override init, but you are not required to.
Also consider changing your initWithObject: name to be more specific, to something like initWithPost: (I'm assuming this is some kind of blog-entry fetcher based on your ivars) so it's more apparent what object is desired.

I think you misinterpreted what you read. I don't think you would throw an exception. You could; however, leak memory. If your initWithObject: method looks like this:
- (id)initWithObject:(id)obj {
if ((self = [self init])) {
self.url=[obj url];
self.blurb=[obj blurb];
self.author=[obj author];
}
return self;
}
And you would be perfectly fine. You could get an exception if your object was instantiated with -init and you used a variable which was assigned, assuming it was real. So in your subsequent methods be sure to check that the objects exist before using them.
If you made your object with -init rather than -initWithObject this could throw an exception:
- (void)dealloc {
[url release];
[blurb release];
[author release];
[super dealloc];
}

The rule that Apple has established for Cocoa programming is that every class must have one initializer method which is the "Designated Initializer". Every other initializer for the class must call that D.I.* The D.I. itself must call the superclass's D.I. Generally, the initializer with the greatest number of arguments (the one that most completely specifies the state of the new object) is the D.I.
In your case, with the bare init, and initWithObject:, the second would likely be the D.I. You would therefore override init to call initWithObject: with some default argument:
- (id) init {
return [self initWithObject:[Object objectWithURL:[NSURL URLWithString:#"http://www.apple.com"]
blurb:#""
author:#""]];
}
This will result in a sort of dummy object, which is correctly initialized with useless data. (Outside of ARC, be sure to watch the memory management of the default argument(s) -- you want to use an autoreleased/unowned object(s).)
*Sometimes an exception is made for initWithCoder:.

If you have any method that you don't want called and that you don't want your subclass to support, throwing an exception in a Debug build is perfectly reasonable.

Related

Calling init in init, before self = [super init]

It is a common init pattern to do self = [super init]; which should assign the self pointer
But can i forward the init like this?
- (id)initWithObject:(id)object {
return [self initWithObject:object scrollTo:nil];
}
The code works, but not sure if it is Kosher... and also how can it work without self = [super init]
moving further, is this ok?
- (id)initWithObject:(id)object {
self = [self initWithObject:object scrollTo:nil]; // NOT super
if (self) {
//...
}
return self;
}
Yes, this is fine. I have done this myself a couple of times without problems, and I found a code example in the Apple documentation (scroll down to "Multiple Initializers and the Designated Initializer").
It is absolutely legitimate only if in return operator you call designated initializer or initializer which calls the one. Make sure that one way or other the designated initializer is called.
Yes, sure you can! Note that initWithObject:scrollTo has to return a valid self object.

calling super from within a GCD dispatch_async block: is it safe?

I'm in a bit of a pickle. I know that calling [self methodName] from within a block will lead to a retain cycle.
However in this class due to multithreading I cannot allow execution of the method that the block is accessing from anywhere else other than the block, as it would potentially lead to serious problems.
Current code:
if (_getRunning==NO){
__weak SyncArrayMT *_weak_self = self;
_get_t = ^void (void){
_weak_self->_getRunning = YES;
NSArray *objects = [_weak_self get:getQuery
usingClassCtor:ctor
withAuthBlock:authBlock];
if (_weak_self.getBlockCb)
_weak_self.getBlockCb(objects);
_weak_self->_getRunning = NO;
};
}
Does exactly that, it calls [self getmethod]. While its ok for the dispatched block to run this method, I do not want anything outside this class calling this method.
So, would it be ok to override this inherited method as such:
- (NSArray *) get:(NSString *)getQuery usingClassCtor:(initBlock)initCb withAuthBlock:(authenticate)authBlock
{
NSLog(#"Direct call to get is not allowed - use the threaded method");
return nil;
}
And then change the block to this:
_get_t = ^void (void){
_weak_self->_getRunning = YES;
NSArray *objects = [super get:getQuery
usingClassCtor:ctor
withAuthBlock:authBlock];
if (_weak_self.getBlockCb)
_weak_self.getBlockCb(objects);
_weak_self->_getRunning = NO;
};
I have tried it and it works without doing a call to the [self getMethod], but will super be retained, properly released, etc? Yes I am using ARC. Would calling super within a block lead to any problem ? Is there a hack to get a __weak to super instead ?
Alternatively, how can I disallow direct calls to [self getMethod] (which is inherited) and only use it internally ?
I know that Objective-C doesn't exactly implement this, but I know there are tricks, such as declaring and implementing a method in the implementation file only.
EDIT#1:
I have tried with SEL & IMP and function pointers. Problem is that IMP and function pointers require as a parameter an instance, and this renders the hole point mute:
NSString * (*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[super methodForSelector:#selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,#selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);
This simply calls the inherited method. Trying to use it with super simply gives an error. At this point I will go ahead and simply use super within the block, and try and profile to see if it leads to any retain cycle.
EDIT#2:
Based on newacct's answer, this is what I ended up doing:
typedef NSArray * (* getFuncPtr)(id,SEL,id,id,id);
...
...
__weak SyncArrayMT *_weak_self = self;
_getMethod = (NSArray * (*)(id,SEL,id,id,id))[[[self class] superclass] instanceMethodForSelector:#selector(get:usingClassCtor:withAuthBlock:)];
_get_t = ^void (void){
NSArray *objects = _weak_self->_getMethod(_weak_self,#selector(get:usingClassCtor:withAuthBlock:),getQuery,ctor,authBlock);
}
I am hoping this should avoid any retain cycles, although I haven't actually profiled it yet.
I know that calling [self methodName] from within a block will lead to
a retain cycle.
That is not true in general. The block will retain self, yes. But there will only be a "retain cycle" if self somehow retains the block. In this case, it does.
but will super be retained
Yes, self will be retained (super is a call on self with a different method lookup pathway).
I have tried with SEL & IMP and function pointers. Problem is that IMP
and function pointers require as a parameter an instance, and this
renders the hole point mute:
NSString * (*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[super methodForSelector:#selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,#selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);
This simply calls the inherited method. Trying to use it with super simply gives an error. At this point I will go ahead and simply use super within the block, and try and profile to see if it leads to any retain cycle.
There are many wrong points here. First, as said above, super is a call on self (there is no such thing as a super object), so it would be sufficient to get the IMP for the method in the superclass, and call it on self.
BUT, [super methodForSelector:... does not get the method in the superclass. It actually gets the method in this class. The super in [super methodForSelector:... affects which methodForSelector: method is called. However, no class ever overrides methodForSelector:, so there is actually no difference between [super methodForSelector:... and [self methodForSelector:.... As said above, super calls the method on self, so it still finds the method based on the class of the current object.
You can get the right IMP by using the class method +instanceMethodForSelector::
NSString *(*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[[[self class] superclass] instanceMethodForSelector:#selector(sendObjectsPassingTest:withAuthBlock:)];
However, using the above will not work correctly if the current object is an instance of a subclass, because then [self class] will be the subclass. So to make sure it does what we want, we need to hard-code the name of our current class, or the superclass:
NSString *(*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[[SyncArrayMT superclass] instanceMethodForSelector:#selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,#selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);
It is also possible to do it using objc_msgSendSuper directly, but that function is not really that easy to use either. So I think you should stick with the IMP approach above.

What happens to alloc if initWithX fails?

It is common practice to write MyClass* obj = [[MyClass alloc] initWithX:X] in Objective-C. initWithX is usually defined as
- (MyClass*) initWithX: (MyArgClass*) X {
if (self = [super init]) {
// initialize
}
return self;
}
My question is: what if initialize fails? I don't want to throw exceptions, but, how do I indicate error? If I return nil, the caller will not be able to release the pointer.
If initialization fails for any reason you should release self. For an exception that may occur in your initialization you need to add you #try #catch as appropriate so you can release self.
- (MyClass*) initWithX: (MyArgClass*) X {
if (self = [super init]) {
// initialize
if(myInitializationCodeFailed)
{
[self release];
return nil;
}
}
return self;
}
Update
If it is possible for your initialization fail I would not raise an exception from with in your initialization code. If you would like to provide the caller with information I would refactor the initializer to accept an NSError to be returned.
- (MyClass*) initWithX: (MyArgClass*) X error:(NSError**)error {
As Alexei Sholik points in the comments check out the Handling Initialization Failure section of Allocating and Initializing Objects.
Basically, this answers your question.
Handling Initialization Failure
In general, if there is a problem during an initialization method, you should call the release method on self and return nil.
There are two main consequences of this policy:
Any object (whether your own class, a subclass, or an external caller) that receives nil from an initializer method should be able to deal with it. In the unlikely case that the caller has established any external references to the object before the call, you must undo any connections.
You must make sure that dealloc methods are safe in the presence of partially initialized objects.
...

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.

Returning other objects on init

I've read in many places that you should always initialize Objective-C objects like so:
- (id) init {
if (self = [super init]) {
....
}
return self;
}
Because the super's init method may return a separate object from the current self.
Now I'm trying to do something like this, and I'm not sure if I have it right, vis-a-vis how retaining and releasing should work:
- (id) init:(int)idx {
id obj = [Cache findSelf:idx];
if (obj) {
[self release];
self = [obj retain];
} else {
self = [self doLoad];
}
return self;
}
I'm mostly curious if this is the correct way to do the retaining and releasing of self and obj. Is there a better way?
You're correct about the self = [super init] part, since some Cocoa classes actually do return a different object than the one that was allocated. However, this is the exception rather than the rule, and doing so in your own code should be exceedingly rare or not done at all. Although it may be tempting to intercept -init calls, you'd be going against the grain of established convention and what Objective-C programmers expect the code to do.
This type of -init method is generally a bad approach, since -init methods should be as straightforward as possible, and should really be concerned with initializing the object. I'd probably write a convenience method like this:
+ (id) instanceForIndex:(NSUInteger)index {
id obj = [Cache findSelf:index];
if (obj == nil) {
obj = [[self alloc] init];
// Add to cache
}
return [[object retain] autorelease];
}
Then call this method instead of -init. This will make the -init logic much cleaner.
Also, I'm not sure what your Cache class does, but it could be worth rethinking that implementation, and using a hidden static variable to store instances (for example, an NSMutableDictionary, where the key is an NSNumber created from the index). This SO question may be of use.
I agree with Quinn that you should use a convenience class method. Still, I think that your init method is mostly correct, except in your else clause you need to call the parent initializer, i.e. self = [super init].