Bail out of an object's init with arc? - objective-c

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;
}

Related

Objective-C: Why not call the designated initializer?

I've inherited this code:
- (id)initWithLocation:(CLLocation *)inLocation {
if (self = [super init])
{
_location = [inLocation copy];
}
return self;
}
- (id)initWithLocation:(CLLocation *)inLocation offsetValue:(NSNumber *)offset {
if (self = [super init])
{
_location = [inLocation copy];
_offset = [offset copy];
}
return self;
}
and am wondering if there's a good reason why the first method does not call the designated initializer (e.g. like this Is it okay to call an init method in self, in an init method?)?
i.e. why not do this:
- (id)initWithLocation:(CLLocation *)inLocation {
if (self = [super init])
{
[self initWithLocation:inLocation offsetValue:nil];
}
return self;
}
- (id)initWithLocation:(CLLocation *)inLocation offsetValue:(NSNumber *)offset {
if (self = [super init])
{
_location = [inLocation copy];
_offset = [offset copy];
}
return self;
}
The - (id)initWithLocation:(CLLocation *)inLocation offsetValue:(NSNumber *)offset method should be a designated initializer and the - (id)initWithLocation:(CLLocation *)inLocation should call it like this:
- (id)initWithLocation:(CLLocation *)inLocation {
return [self initWithLocation:inLocation offsetValue:nil];
}
It's also considered a good practice to mark a designated initializer in the class interface using NS_DESIGNATED_INITIALIZER:
- (id)initWithLocation:(CLLocation *)inLocation offsetValue:(NSNumber *)offset NS_DESIGNATED_INITIALIZER;
the much more appropriate way would be like this:
- (id)initWithLocation:(CLLocation *)inLocation {
return [self initWithLocation:inLocation offsetValue:nil];
}
- (id)initWithLocation:(CLLocation *)inLocation offsetValue:(NSNumber *)offset {
if (self = [super init]) {
_location = [inLocation copy];
_offset = [offset copy];
}
return self;
}
All you actually need to do is...
- (id)initWithLocation:(CLLocation *)inLocation {
return [self initWithLocation:inLocation offsetValue:nil];
}
- (id)initWithLocation:(CLLocation *)inLocation offsetValue:(NSNumber *)offset {
if (self = [super init])
{
_location = [inLocation copy];
_offset = [offset copy];
}
return self;
}
And you're right. There is no reason not to in this case.

How to fix "instance variable while 'self'"

How to fix this analysis issue:
CODE
if(![super initWithFrame:CGRectZero]){
return nil;
}
firstOfPrev = -1;
marks = markArray;
monthDate = date;
startOnSunday = sunday;
You're getting the error because you're never assigning the result of the super initializer call to self. I believe this is what you want:
- (instancetype)initWithFrame:(CGRect)frame
{
if (!(self = [super initWithFrame:frame])) {
return nil;
}
// do stuff
return self;
}
However, this may be more clear if you write out your initializers like this:
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// do stuff
}
return self;
}

non-ARC: Should I call [super init] before [self release]?

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.

Init class as subclass

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;
}

Confuse in one objective -c Singleton example

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;