I'm pretty new to Objective-C and I have a question.
I have created a custom class and tried to create overloads for Initialization:
- (id)init
{
if (self = [super init]) {
[self setIsCurrentCar:NO];
}
return self;
}
-(id) initWithID:(NSInteger)id {
if(self = [self init]) {
[self setID:id];
}
return self;
}
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if(self = [self initWithID:id]) {
[self setCarYear:year];
}
return self;
}
Let's say at one point, I call the -(id) initWithIDCarYear method.
I'd like to know the code above is structurally correct.
In this code, self is set for 3 times. Is there a better
solution?
Do I have memory leak in this code? (using ARC)
Do I have to check for if(self = ...) always or it is a
redundant code?
Thank you
#Edit
Is the following code better?
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if (self = [super init]) {
[self setIsCurrentCar:NO];
[self setID:id];
[self setCarYear:year];
}
return self;
}
While your code is ok, i would structure the init-calls in the reverse order, where the most detailed one is the designated initializer and the more general ones would bubble some default values up:
-(id) initWithID:(NSInteger)id
CarYear:(NSString *)year
{
if(self = [super init]) {
_year = year;
_id = id;
}
return self;
}
-(id)initWithID:(NSInteger)id
{
return [self initWithID:id CarYear:#"unset"];
}
-(id)init
{
return [self initWithID:0];
}
if calling one of the more general initializer would generate an illegal state, you could instead throw an error to prohibit using it.
let's assume, a car needs to have a ID, but not a year. It would be ok to use initWithID but using init would lead to an inconsistent state, so we want to force not to use it:
-(id)init
{
[NSException raise:NSInternalInconsistencyException
format:#"You must use -initWithID: or -initWithID:CarYear:", NSStringFromSelector(_cmd)];
return nil;
}
In this code, self is set for 3 times. Is there a better solution?
see above
Do I have memory leak in this code? (using ARC)
No, everything is fine
Do I have to check for if(self = ...) always or it is a redundant code?
As I showed you: you can call different init methods in a chain. just the last in that chain needs to perform that.
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if (self = [super init]) {
[self setIsCurrentCar:NO];
[self setID:id];
[self setCarYear:year];
}
return self;
}
You should not use setters on self in init-methods, see Apple's docs.
I'd like to know the code above is structurally correct.
Yes. I don't see any problem with it.
In this code, self is set for 3 times. Is there a better solution?
That's pretty normal. I wouldn't bother changing that.
Do I have memory leak in this code? (using ARC)
No.
Do I have to check for if (self = ...) always or it is a redundant
code?
You don't have to, but you definitely should. See this question for details.
It looks like you have one mandatory initialized variable and two that are effectively optional.
I'd recommend implementing an init method and two #property()s for the ID and carYear. That reduces the # of initializers and better reflects that usage contract of the class.
From my little knowledge... tried to create overloads for Initialization this statement should not been used here.
As typically overload means same name multiple arguments, but in obj-c we do not follow this. In obj-c overloading is faked by naming the parameters.
So here you have 3 different sets of code, and each one is called some other method. And you do not have a memory leak as you are not allocating memory thrice for same object, instead you are initializing it.
Related
I would like to set a member variable in a derived object before i call [super init].
All I can find is that you should not do such a thing. My worakround, to do it anyhow, works, but actually I like to know what the consequences are when bending the rules. Or even better if there is a correct way to deal with this.
The Details:
I have several wrappers that bind a c++ object to an objective-c objec (mostly UI...View or UI...Controller)
#interface my_scrollview : UIScrollView
{
my_c_class* m_p;
}
-(id) initWithFrame:(CGRect)frame wrapper: (my_scrollview*) pWrap;
-(void) setContentOffset:(CGPoint)contentOffset;
#end
#implementation dwin_scrollview_ios
-(id) initWithFrame:(CGRect)frame wrapper: (my_scrollview*) pWrap
{
m_p = pWrap; // illegal but works?
return [super initWithFrame: frame];
//m_p = pWrap; // to late because [super init...] already called overriden func.
}
In my overwritten setContentOffset-method I need to access my C++-Object.
The Problem arises because the initWithFrame internally initializes its content using setContentOffset. So this method is called before I could "legaly" set up the link to my c++-object.
I can implement my overrides with a check if m_p is set(luckily it's initialized to nil). But I have to synchronize the state of the view and my c++-object after the the init-method. In this example this is no big deal but other such realtions are much more complicated and I end up with lot of code that repeats steps of the initialization or otherwise brings me back in sync, although before the [super init...] I know I was in sync.
Is there a pattern to solve this correct (and elegant)?
Is it really so bad to int the pointer before the call to [super init..];?
(I assume one consequence is that this crashes if [super init] returns nil...? any other cases?)
Thanks in advance
Moritz
There is nothing magical about init methods in Objective-C. alloc returns an object of the class that you want, with all instance variables initialized to 0 / nil / NULL / 0.0 etc. Each init method then just executes the code that the developer has written.
There are things that are obviously stupid, like setting an instance variable of the superclass, then calling [super init] which promptly overwrites it. And you need to be aware that init doesn't necessarily return self, but a different object, in which case everything you've initialised in the base class before calling [super init] will be gone.
// illegal but works?
No, it's not illegal. It's perfectly legitimate, although unconventional, to do stuff to an object before its superclass' initializer has been run. It may still lead to unexpected behavior and bugs, but it's not illegal per se. Sometimes it's even necessary, for example when you want to perform some computation and delegate the result of that computation to the superclass' initializer.
You are using wrong init
try this:
-(id) initWithFrame:(CGRect)frame wrapper: (my_scrollview*) pWrap
{
self = [super initWithFrame: frame];
if (self) {
m_p = pWrap;
}
return self;
}
I intend to call default init method in init method with arguments, in iOS. like this:
-(id)init{
self = [super init];
if (self) {
Office = [[NSString alloc]init];
}
return self;
}
-(id)initWithOffice:(NSString*)office{
self = [self init];
if (self) {
self.Office = itemDescription;
}
return self;
}
My question is it a good practice, What should be done?
I appreciate your response in advance,
That will work, but I would prefer the following as it doesn't allocate an empty string, only to be replaced with the initializing string:
-(id)initWithOffice:(NSString*)office{
self = [super init]; // Not [self init]
if (self) {
Office = office; // OK if using ARC
}
return self;
}
The first init method doesn't make a great deal of sense; I think simply leaving Office as nil is better (NSString objects are immutable). As pointed out by #H2CO3, the initWithOffice method becomes the designated initializer for the class and all other init methods should use it to initialize the object. With that in mind the first init method should be:
-(id)init{
return [self initWithOffice:nil];
}
Creating a method starts with initWith is to see what values will be passed. It helps you to remind which values should be sent and allocated in the method. Consider you have 4 variables to init when the view is initialized. It's best to keep a separate initWith method where you can init your view and other variables you customize.
I think you should improve you object assignning logic,Like that...
-(id)initWithOffice:(NSString*)office{
self = [self init];
if (self) {
self.Office = [[NSString alloc] initWithString:office]; //Purpose is that //the office object can only be released by the self, non other classes (The owner //of the variable should be self).
}
return self;
}
I've been creating custom initializers for my objects just because it feels like better practice than setting their variables in other ways. In these initializers I usually set the variables of the object then return a call to the main init.
So, for example, in a UIViewController subclass my code would look something like this:
-(id)initWithValue:(int)val {
self.value = val;
return [self initWithNibName:nil bundle:nil];
}
where value is an integer that belongs to that ViewController subclass, and there are usually more values than that.
However, recently I started setting self first because I thought that the self = [self init...] would replace the current instance of the class and thus I would lose that instance of self.
So, I have started doing:
-(id)initWithValue:(int)val {
self = [self initWithNibName:nil bundle:nil];
self.value = val;
return self;
}
I then recently checked the original version and realized that everything does work properly and the change was unneccessary.
So, my question is this:
What does the [super initWithNibName:bundle:] do which is causing it to create an object but not replace the original object?
Is one of the two versions better than the other to use or are they both equivalent? If one is better, which should be used?
Thanks in advanced!
You should do it the following way:
- (id)initWithValue:(int)val {
self = [super initWithNibName:nil bundle:nil];
if (self) {
_value = val;
}
return self;
}
In iOS, a common pattern is to return nil if the parameters sent to init methods are invalid. The value will be one of 2 things: the current pointer to self or nil. If the call to super returns nil then the object was not setup properly so you should also return nil. Doing self = [super initWithNibName:nil bundle:nil]; just makes it easier to respect the possible nil value returned by super
Please use the following code to override init method
-(id)initWithValue:(int)val
{
self = [super init];
if(self)
{
self.value = val;
}
return self;
}
The first code which you have written won't store the value, because before creation of the object you are trying to store data.
But, the second code, needed a small modification for better practice i.e, like this...
-(id)initWithValue:(int)val {
self = [self initWithNibName:nil bundle:nil];
if(self)
_value = val;
return self;
}
Hope this is useful to you...:-)
the [super initWithNibName:bundle:] actually invoke the method of the superclass.
if you use [self initWithNibName:bundle:] actually it will call the rewriting of initWithNibName:bundle: of course you must have rewrote it,otherwise it will also call the superclass method. so if you want to do some initialize in the rewrite of initWithNibName then you can use [self initWithNibName:bundle:] but if you don't need do the extra initialize there is no difference between the to method;
originally, I have a default init function
-(id) init
{
if (self=[super init])
{
......
}
return self;
}
However, I like to override the init function to pass in custom objects or other objects
like
-(id) initWithScore:(NSString*) score
{
if (self=[super init])
Now there is an error saying [super init] function can only be called with -(id) init function.
So what do I do to fix it so I can pass in objects and also use self=[super init]?
Error:Cannot assign to self outside of a method in the init family.
I was trying to convert a projet to ARC and after creating a new one and including the files from the old one - one of the issues i got was
Cannot assign to 'self' outside of a method in the init family
The selector name MUST begin with init - not only that in my case the init selector was:
-(id)initwithPage:(unsigned)pageNum {...}
Notice the small 'w'.
I have changed it to:
-(id)initWithPage:(unsigned)pageNum {...}
Notice the capital 'W'!
My problem was solved.
I hope this helps someone.
You need to return an object of type id in your new method.
Suppose you have declare an NSString *myscore property, you will write something like this:
-(id) initWithScore:(NSString*) score
{
self=[super init];
if (self)
{
self.myscore = score;
}
return self;
}
Is there a method that is always called in Cocoa? Many classes have init or initWith, but even worse they can be loaded from a nib or something. I don't want to have to scrape around and find how it does this in this case. I just want to set some initial variables and other things, and I want a method to subclass that I can depend on no matter if it's a UIView, UIViewController or UITableViewCell etc.
No there is not such a method. init comes from NSObject so every object can use it, and as well subclasses define their own initialization methods. UIView, for example, defines initWithFrame: and furthermore there are init methods from protocols, such as NSCoding which defines initWithCoder:. This is the dynamic nature of objective-C, anything can be extended at any time. That being said, there are some patterns. UIViewController almost always takes initWithNibName:bundle: and UIView almost always takes initWithFrame: or initWithCoder:. What I do is make an internal initialize method, and just have the other inits call it.
- (void)initialize
{
//Do stuff
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self initialize];
}
}
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if(self)
{
[self initialize];
}
}
Not 100% sure that it is always called, but I am pretty sure that this is a viable option. To be perfectly honest, I can't recall that I have ever seen this method used in practice and I usually shy away from using this method (I have absolutely no idea why, probably because it's just not the cleanest and most comprehensive method to achieve this...):
-didMoveToSuperview()
From documentation:
Tells the view that its superview changed.
The default implementation of this method does nothing. Subclasses can override it to perform additional actions whenever the superview changes.
There's many ways you can write a custom initializer.
- (id)initWithString:(NSString *)string {
if((self == [super init])) {
self.string = string;
}
return self;
}
That's just how I write my initializers in general. For example, the one above takes a string. (you don't have to pass strings if you don't want).
Btw, init is a method. According to the header for NSObject, init has a method implementation.