I have a model for example Product. Product has required property id.
So I thought that model can't be without property.
My implementation of init method for Product class.
- (instancetype)initWithProductId:(NSNumber *)productId {
if ([productId integerValue] <= 0) {
return nil;
}
self = [super init];
if (self) {
_productId = productId;
}
return self;
}
Is it legal to return nil inside initilization methods? Will I get memory management problems?
And could some one explain why it's legal and when I should use it.
Almost. Obviously someone has called alloc to create an unitialised object, and is now calling your init method. With your code, the super class init will not be called. When the object is deallocated, the super class dealloc is called. But that might fail if superclass init was never called, depending on the implementation of your superclass. So the correct way would be:
if ((self = [super init]) != nil) {
if (productId.integerValue <= 0)
return nil;
_productId = productId;
}
return self;
Related
As we all know, factory methods can't call instance methods. Why does the code below work?
// .m file implementation DemoClass
// custom instance init method
- (instancetype)initWithDate:(NSDate *)date {
if (self = [super init]) {
self.lastTime = date;
}
return self;
}
// custom factory method
+ (instancetype)DemoClassWithDate:(NSDate *)date
//here calling instance method initWithDate:
return [[self alloc] initWithDate:date];
}
[self alloc] will return an instance. initWithDate is just an instance method. No reason why a class method wouldn't be allowed to call an instance method on an instance.
PS. I would highly recommend that you check your compiler settings and tell the compiler to give you a warning if the result of '=' is used as a boolean value. This will prevent many hard to find bugs. You'll have to change the if to
if ((self = [super init]) != nil)
Because it has a reference to the, newly created, instance:
return [[self alloc] initWithDate:date];
// ^^^^^^^^^^^^
// reference
I'm designing a class that MUST only have one instance of itself at any time. I'm trying to avoid the common pattern of a shared singleton that is globally accessible, I only want a local object that can only be allocated once but can also be set to nil. How does this look?
static BOOL isInitialized = NO;
#implementation Single
-(instancetype) init
{
if (isInitialized == NO)
{
if (self = [super init])
{
}
isInitialized = YES;
return self;
}
else
{
NSAssert(FALSE, #"Only one instance allowed");
return nil;
}
}
-(void) dealloc
{
isInitialized = NO;
}
#end
I'm not concerned about thread safety as I only plan to use class on main thread. When the object is deferrenced the overridden dealloc should ensure a new instance can be created. Anybody see any issues with this or improvements? Cheers
It's a corner case, but as written if the call to [super init] fails then isInitialized will be set to YES (Note that in this case the passed in self is deallocated before the flag is set) - this means that no instance of the class will ever be created as there is nothing to deallocate to reset your flag. Maybe you want:
if (self = [super init])
{
isInitialized = YES;
}
return self;
Otherwise, given that you are not concerned over thread safety and you want a second allocation attempt to be a failure (your use of NSAssert), your code looks fine.
Would it be better to return the existing instance instead of crashing?
#implementation Single
- (instancetype)init {
static __weak Single *weakInstance;
Single *strongInstance = weakInstance;
if (strongInstance) {
self = strongInstance;
} else {
if (self = [super init]) {
weakInstance = self;
}
}
return self;
}
You don't need to do anything special in dealloc because the system will clear the __weak reference automatically when the instance is deallocated.
#Rob
Can the strong reference be removed from your method?
- (instancetype)init
{
static __weak id weakInstance;
if (weakInstance)
{
self = weakInstance;
}
else
{
if (self = [super init])
{
weakInstance = self;
}
}
return self;
}
seems to work.
As follow-up of sorts to Is returning nil from a [[class alloc] init] considered good practice?, there's a case that I haven't seen any discussed much: what to do with an init that fails some preconditions before it can call the next init?
Example, suppose in this initWithStuff: method being passed nil or in general having no value to pass to initWithValue: is an absolute failure and we definitely want to return nil.
- (id)initWithStuff:(Stuff *)inStuff {
if (!inStuff || ![inStuff hasValidValue])
{
// can't proceed to call initWithValue: because we have no value
// so do what?
return nil;
}
NSInteger value = [inStuff integerValue];
return [super initWithValue:value];
}
Perhaps a clearer example is if the designated initializer method we wrap takes an object pointer and throws an exception if its passed nil. We definitely need to short-circuit that init call that would cause an exception.
My guess: init by any means possible, and only then release self before returning nil. If necessary, call bare init or any other initializer that will work to finish putting self into a known state before releasing it.
// can't proceed to call super's initWithValue: because we have no value
// so do what? do this:
self = [super init]; // or initWithValue:0
[self release];
return nil;
And if there were no such initializer that will work without valid data, I guess one would need to construct some valid, dummy data. Or complain to its author and until then just return nil and live with the leak :^)
Also, how does ARC affect the situation?
My guess: still finish init by any means possible, then just return nil. You'd think setting self might be redundant, but in some cases it's not. In any case, it but it needs to be there to silence a compiler warning.
// can't proceed to call super's initWithValue: because we have no value
// so do what? do this:
self = [super init]; // finish init so ARC can release it having no strong references
return nil;
Are my guesses wrong in any way?
Ideally, if a precondition fails, you don't call [super init…]. You just release self (if not using ARC) and return nil:
- (id)initWithStuff:(Stuff *)stuff {
if (!stuff || ![stuff isValid]) {
[self release]; // if not using ARC
return nil;
}
if (self = [super init]) {
// initialization here
}
return self;
}
The release takes care of deallocating self under MRC. Under ARC, the compiler will insert the release for you.
However, there is a potential problem with this approach. When you release self (or when ARC releases it for you), the system will send the dealloc message to the object. And your dealloc method will call [super dealloc]. You could suppress the [super dealloc] under MRC, but you can't avoid it with ARC.
So the danger is that your superclass might assume that one of its instance variables has been initialized, and rely on that initialized value in its dealloc. For example, suppose this is the superclass:
#interface SomeSuperclass : NSObject
#end
#implementation SomeSuperclass {
CFMutableBagRef bag;
}
- (id)init {
if (self = [super init]) {
bag = CFBagCreateMutable(NULL, 0, &kCFTypeBagCallBacks);
}
return self;
}
- (void)dealloc {
CFRelease(bag);
}
#end
The problem here is that CFRelease requires its argument to not be nil. So this will crash during deallocation if you don't call [super init] in your subclass.
Given this problem, I have to change my initial recommendation. If you know that your superclass's dealloc doesn't have this sort of problem (because, for example, it checks pointers before dereferencing them or passing them to CFRelease), then you can safely not call [super init].
If you don't know that your superclass's dealloc is safe, then my recommendation is that you move your preconditions out of init and into a class factory method.
In other words, don't treat alloc/init as part of your class's public interface. Provide a class method for creating instances:
// The class factory method. Declare this in your header file. This is how you
// or any user of this class should create instances.
+ (id)myObjectWithStuff:(Stuff *)stuff {
if (!stuff || ![stuff isValid])
return nil;
// self here is the class object, so it's appropriate to send `alloc` to it.
// You don't want to hardcode the class name here because that would break
// subclassing.
return [[self alloc] initWithStuff:stuff];
}
// This is now considered a private method. You should not declare it in your
// header file, though in Objective-C you can't prevent the user from calling it
// if he's determined to.
- (id)initWithStuff:(Stuff *)stuff {
// Precondition was already checked in myObjectWithStuff:.
if (self = [super init]) {
// initialization here...
}
return self;
}
self is a hidden instance variable that points to the current object:
- (id) initWithAmount:(double)theAmount forBudget:(Budget *)aBudget{
if(self = [super init]){
budget = aBudget;
amount = theAmount;
}
return self;
}
is it similar to
this.budget=super.aBudget;
this.amount=super.theAmount;
Normally:
if(control)//true or false
{
//stmts will get executed
}
but instead of returning a true value here the self is assigned with super. Why is it so?
Is it similar to constructor. How to use constructors(default,parameterised and copy) in objective C?
Because an assignment expression also returns the result of the assignment,
if (self = [super init]) { ...
is equivalent to:
self = [super init];
if (self) { ...
And since if does not just test pure boolean values but treats everything that is non-zero, non-NULL or non-nil as true, the if statement tests if self is nil after the assignment.
When we write our own init methods, we are supposed to assign [super init] to self because a init method is free to return a different object than the receiver of the method. If we just called [super init] without assigning it to self, we might initialize a different object than self, which is clearly not desirable.
This form is just the short form of the following code:
- (id)initWithAmount:(double)theAmount forBudget:(Budget *)aBudget {
self = [super init]; // you must do this, because every object inherits all properties
// from the parent class, and they must be initialized
// init can return nil if there was an error during the initialization
// so if the parents initialization failed, there is no point of initializing
// the child object
if (self != nil) {
budget = aBudget;
amount = theAmount;
}
return self;
}
It's the way initializers are defined in Objective-C. It's part of the two-stage creation pattern: seperating memory allocation and initialization of the object. As self references the object instance where the initializer resides in it has to go up the inheritance tree and set what they return to itself, after that it's your turn.
The call of super is only used in the designated initializer. You could have more initializers which should always use the designated initializer.
The pattern
- (id)init {
self = [super init];
if (self) {
// code
}
return self;
}
Is the correct way to handle failures. The test will fail if self = [super init]; returns nil: An error occured in initializers up the inheritance tree, so you don't want to use a failed initialization and skip your own code.
Assume that I instantiate an object of class MyGreatClass in my NIB (as usual by simply dragging an "Object" to the NIB and settings its class to MyGreatClass).
I want access to that instance anywhere in my codebase, without introducing coupling, i.e. without passing objects around like crazy, and without having an outlet to it in, say, [NSApp delegate]. (The latter would make AppDelegate terribly bulky with time.)
I ask: Is the following considered a good code to accomplish this?
//imports
static MyGreatClass *theInstance = nil;
#implementation MyGreatClass
+ (MyGreatClass *)sharedInstance
{
NSAssert(theInstance != nil, #"instance should have been loaded from NIB");
return theInstance;
}
- (id)init //waking up from NIB will call this
{
if (!theInstance)
theInstance = self;
return theInstance;
}
// ...
If this work as expected, I would after the app is loaded be able to access my instance via sharedInstance.
What do you think?
UPDATE: Hmm, on the second thought, the above init method maybe overkill. This is way simpler to think about:
- (id)init
{
NSAssert(!theInstance, #"instance shouldn't exist yet because only "
#"the NIB-awaking process should call this method");
theInstance = self;
return theInstance;
}
Again, what do you think?
The proper way to create a singleton is to override allocWithZone: to ensure another object cannot be created. Overriding init allows the new object to be created, but not initialized. It is thrown away because the init method simply ignores it and returns the object that has been created already. Here is how I would do it:
+ (MyGreatClass *)sharedInstance {
NSAssert(theInstance != nil, #"instance should have been created from NIB");
return theInstance;
}
+ (MyGreatClass *)allocWithZone:(NSZone *)zone {
if(theInstance) return theInstance;
return [[self alloc] init];
}
- (id)init {
if(theInstance) return theInstance;
if(self = [super init]) {
theInstance = self;
// other initialization
}
return self;
}
- (void)release {}
- (void)dealloc {
return;
[super dealloc]; // Prevent compiler from issuing warning for not calling super
}
I overrode release and dealloc to ensure that the singleton would not be deallocated. If you don't do this, you should retain and autorelease it in the sharedInstance method. If you want to support multithreading, you should also synchronize access to the theInstance variable.