I have a custom non-ARC init, and I wonder if I should call [super init] before releasing self.
Solution A, not calling [super init] before [self release]:
- (instancetype)initWithInteger:(NSInteger)anInteger
{
if (anInteger == 0)
{
[self release];
return nil;
}
self = [super init];
if (!self)
return nil;
// Initialization code
self.i = anInteger;
return self;
}
Solution B, calling [super init] before [self release]:
- (instancetype)initWithInteger:(NSInteger)anInteger
{
self = [super init];
if (!self)
return nil;
if (anInteger == 0)
{
[self release];
return nil;
}
// Initialization code
self.i = anInteger;
return self;
}
I would go with the second pattern. The reason for this is that super's dealloc might possibly rely on something in super's init having been done to work properly.
Here is a (very) contrived example, this is the init and dealloc method in a class that you are subclassing.
#implementation ASuperClass
{
char* foo;
}
-(id) init
{
self = [super init];
if (self != nil)
{
foo = strdup("blah blah blah");
}
return self;
}
-(void) dealloc
{
if (foo[1] == 'l')
{
NSLog(#"Second character was l");
}
free(foo);
}
In the above, if this is the class you inherited from, your first pattern will throw an EXC_BAD_ACCESS on a null pointer dereference.
Related
Assuming our -init method only invokes messages on self, why is it common to check if self != nil if messaging nil has no effect?
Let's say we have an initializer as follows:
- (id)init
{
self = [super init];
if (self) {
[self doThis];
[self setFoo:#"Bar"];
}
return self;
}
Instead of checking self, we could write:
- (id)init
{
self = [super init];
[self doThis];
[self setFoo:#"Bar"];
return self;
}
Now if for some reason [super init] returns nil, there would be no difference in the outcome of the method as far as I know. Why then do we constantly perform this check?
You can send a message to nil, but you cannot access the instance variables of nil. You'll get an EXC_BAD_ACCESS exception.
Consider a class that has instance variables:
#implementation MyObject {
int instanceVariable;
}
- (id)init {
self = [super init];
instanceVariable = 7;
return self;
}
What happens if [super init] returns nil in this example? You will try to access that instanceVariable off of a null pointer and you will get an exception.
Even if you're not accessing any instance variables, other things can certainly go wrong if you don't check for self == nil. You can easily leak malloc-allocated memory or file handles, or pass yourself to some method that's not expecting nil.
Other answers claim that you can leak objects if you don't check for nil. For example:
#implementation MyObject
#synthesize someProperty; // assume it's an NSObject *
- (id)init {
self = [super init];
[self setSomeProperty:[[NSObject alloc] init]];
return self;
}
This won't leak under ARC, even if self is nil. Under manual reference counting (MRC), this example will leak whether self is nil or not, because there's nothing to balance the +1 retain count from [NSObject alloc].
The proper way to do it under MRC is this:
- (id)init {
self = [super init];
[self setSomeProperty:[[[NSObject alloc] init] autorelease]];
}
or this:
- (id)init {
self = [super init];
NSObject *object = [[NSObject alloc] init];
[self setSomeProperty:object];
[object release];
return self;
}
Neither of those will leak, whether self is nil or not.
If you bypass the setter method, like this, you'll just crash if self is nil:
- (id)init {
self = [super init];
_someProperty = [[NSObject alloc] init];
return self;
}
If [super init] did in turn return nil, then you would end up possibly allocating more objects that will never be used, since when you return self, you will return nil; So those objects will not be released.
My question is kind of weird, lets say I have a class inheriting from UITableViewCell called GenericTableViewCell and some more classes inheriting from GenericTableViewCell.
I want to be able to pass an argument to the GenericTableViewCell init method that will tell me which subclass of GenericTableViewCell should init this TableViewCell as.
Heres what I thought of but I know that it will fail because it has a recursive loop in it.
#implementation GenericTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier cellIdentifier: (CellIdentifier *) identifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
if ([identifier class] == [FirstIdentifier class]){
self = [[FirstTableViewCell alloc] initWithStyle:style reuseIdentifier:reuseIdentifier];
}
/// more else statements to check for other identifier cases
}
return self;
}
#end
Is there any way to do this? Or should I just check the identifier outside of the init function and by that decide which cell do I declare?
Yes, you can do this. It's a pattern you sometimes come across called a class cluster. If you are not using ARC, you must release the original value of self to stop a memory leak.
However, I would not do this. I would create a factory method in GenericTableViewCell.
+(GenericTableViewCell*) cellWithStyle: (UITableViewCellStyle)style
reuseIdentifier: (NSString *)reuseIdentifier
cellIdentifier: (CellIdentifier *) identifier
{
GenericTableViewCell* ret = nil;
if ([identifier class] == [FirstIdentifier class])
{
ret = [[FirstTableViewCell alloc] initWithStyle:style reuseIdentifier:reuseIdentifier];
}
else
{
// ....
}
return [ret autorelease];
}
You can eliminate the if statement by adding a method to CellIdentifier and overriding it in subclasseslike this:
// in CellIdentifier.m
-(id) classForCell
{
return [GenericTableViewCell class];
}
// in FirstIdentifier.m
-(id) classForCell
{
return [FirstTableViewCell class];
}
Then your factory method becomes
+(GenericTableViewCell*) cellWithStyle: (UITableViewCellStyle)style
reuseIdentifier: (NSString *)reuseIdentifier
cellIdentifier: (CellIdentifier *) identifier
{
return [[[[identifier classForCell] alloc] initWithStyle:style
reuseIdentifier:reuseIdentifier] autorelease];
}
The thing you should implement is called class cluster pattern. In your situation you shouldn't call initWithStyle:reuseIdentifier: on a subclass rather than in subclass' initializer:
In GenericTableViewCell:
- (id)initWithCustomIdentifier:(NSString *identifier) {
Class cellClass = NSClassFromString(identifier);
if (!cellClass) {
cellClass = [MyStandardTableViewCell class];
}
self = [[cellClass alloc] init];
return self;
}
In MyStandardTableViewCell (or any other subclass of GenericTableViewCell:
- (id)init {
self = [super initWithStyle:someStyle reuseIdentifier:NSStringFromClass([self class])];
if (!self) return nil;
// do extra setup here
return self;
}
I have been reading about an objective-c singleton example from http://getsetgames.com/2009/08/30/the-objective-c-singleton/.
The .m code looks like
+(MySingleton*)sharedMySingleton{
#synchronized([MySingleton class])
{if (!_sharedMySingleton)
if(!_sharedMySingleton)
return _shareMySingleton;
}
....
-(id)init{
self = [super init];
if(self != nil){}
return self;
}
....
Thanks for your example, but I have a confused place, in your code, I am wondering in which place the static MySingleton* _sharedMySingleton is initialized. for example if we would have to have some implementation like
-(id)init{
self = [super init];
if(self != nil){
_sharedMySingleton = self
}
return self;
}
You miscopied a part of the code from the link you posted by mistake.
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
[[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([MySingleton class])
{
NSAssert(_sharedMySingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMySingleton = [super alloc];
return _sharedMySingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
}
return self;
}
-(void)sayHello {
NSLog(#"Hello World!");
}
#end
looking at the code from the post you are asking about it actually makes sense.
What he is doing in the static method +(MySingleton*)sharedMySingleton is that he is checking if the _sharedMySingletonobject has a value he is returning it, if not it gets initialized.
The alloc method is the one setting the singleton object, its not being set in the initializer. technically its the same, since its going to be point on the same object that will be initialized a moment after.
I hope that clarifies your confusion.
you can use macro from http://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/Foundation/GTMObjectSingleton.h it is vvveeerryyy easy to use
in implementation file (m)
GTMOBJECT_SINGLETON_BOILERPLATE(ClassName, sharedInstance)
and header file
+ (ClassName *) sharedInstance;
How do I bail out of creating an object with ARC?
I'm looking for the ARC equivalent of this (from memory):
- (id)init
{
if (( self = [super init] )) {
if (!condition) {
[self release];
self = nil;
return self;
}
}
return self;
}
Just get rid of the call to release and you'll be fine. Since you nil self, there will be no more references to the old self so it will be deallocated.
- (id)init;
{
if ((self = [super init])) {
if (!condition) {
return nil;
}
}
return self;
}
If I create a custom initWith for an object do I essentially include the code I would add should I want to override init?
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
}
return(self);
}
e.g.
-(id) initWithX:(int) inPosX andY:(int) inPosY {
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return(self);
}
gary
You can create one designated initializer that accepts all parameters that you want to make available in initialization.
Then you call from your other -(id)init your designated initializer with proper parameters.
Only the designated initializer will initialize super class [super init].
Example:
- (id)init
{
return [self initWithX:defaultX andY:defaultY];
}
- (id)initWithPosition:(NSPoint)position
{
return [self initWithX:position.x andY:position.y];
}
- (id)initWithX:(int)inPosX andY:(int)inPosY
{
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return self;
}
The designated initializer is -(id)initWithX:andY: and you call it from other initializers.
In case you want to extend this class you call your designated initializer from subclass.
I'd suggest creating one main initializer that handles most of the work. You can then create any number of other initializers that all call this main one. The advantage of this is if you want to change the initialization process, you'll only have to change one spot. It might look like this:
-(id) initWithX:(float)x {
if (self = [super init]) {
/* do most of initialization */
self.xVal = x;
}
return self;
}
-(id) init {
return [self initWithX:0.0f];
}
In this example initWithX: is our main initializer. The other initializer (init) simply calls initWithX: with a default value (in this case 0).
Yes, that's exactly how I do it. One slight change will cut out a line of code:
if (self = [super init]) {
As opposed to:
self = [super init];
if(self) {
For modern Objective-C ...
UDFile.h
#import <Foundation/Foundation.h>
#interface UDFile : NSObject
#property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
#end
UDFile.m
#import "UDFile.h"
#implementation UDFile
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = [name copy];
}
return self;
}
- (instancetype)init {
return [self initWithPathname:#""];
}
Sometimes, you want to reuse some initialisation code and modify the behaviour only slightly for specific initialisers. In this case, I do the following:
- (id) init
{
self = [super init];
if (!self) return nil;
// These values are always initialised this way
ivar1 = 10;
ivar2 = #"HellO";
ivar3 = [[NSMutableArray alloc] initWithCapacity:10];
ivar4 = 22;
return self;
}
- (id) initWithIvar4:(int) aValue
{
// call -init on self, which will call -init on super for us, and set
// up ivar1, ivar2, ivar3, and ivar4.
self = [self init];
if (!self) return nil;
// Change ivar4 from the default 22 to whatever aValue is.
ivar4 = aValue;
return self;
}