Today I was going through some of the online codes and I found some NSObject Class.
Some are having:
- (id)init
{
self = [super init];
return self;
}
And some doesn't have it.
Then I also tried this in my Sample Code. I was totally confused. Before this I was thinking that we must write -(id)init method to instantiate a NSObject Class. But now it is working without it.
Here are my some of my doubts:
is -(id)init method really necessary?
What happens if we don't use it.
If we don't write init method even then my code works. Why?
Having an empty -init has absolutely no function.
NSObject already has -init method. And methods in Objective-C are inherited.
It's the same as overriding any other method and just call super:
- (void)someMethod {
[super someMethod];
}
You'll find answers to your questions in the Apple documentation on initializers. I'd recommend you go through those materials, they will clarify a lot of things.
To sum up the documentation with regards to your questions:
is -(id)init method really necessary?
If you don't need to perform extra logic when your class is instanced, then you don't need to implement an init in your class.
What happens if we don't use it.
Even if you don't need to implement it in your subclasses, you need to call it when creating a new object, e.g. [[MyClass alloc] init]
If we don't write init method even then my code works. Why?
As I wrote above, you don't need to declare one for your class, but you need to call it. Otherwise your code might not properly work.
[super init] is highly recommended to give a chance for the superclass to also properly configure the newly created object.
Not calling super init is an error, and results are undefined. (Bad things my happen)
-init() is initializer of NSObject. If a user creates a Person Class which inherits NSObject. It can override this initializer, or one can create their own custom initializer as show below
eg.
class Person : NSObject
{
let name:String
//Initializer inherited from NSObject
override init()
{
super.init()
}
//Custom Initializer
init(withName name:String)
{
super.init()
self.name = name
}
}
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 want to subclass UIBarButtonItem with an initializer that automatically sets the image, target and selector.
I tried to override the - (id) init message like this:
- (id) init {
self = [super initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:#selector(myMessage)];
if (self) {
//Custom initialization
}
return self;
}
This results in an unwanted recursion, because initWithBarButtonSystemItem:target:action calls init.
I read the answer to the following question, but it didn't help me as it still gives me the same recursion:
Custom init method in Objective-C, how to avoid recursion?
The code I used when testing this was the following:
- (id)initWithBarButtonSystemItem:(UIBarButtonSystemItem)systemItem target:(id)target action:(SEL)action {
return [super initWithBarButtonSystemItem:systemItem target:target action:action];
}
- (id) init {
self = [self initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:#selector(myMessage)];
if (self) {
//Custom initialization
}
return self;
}
I have the same requirements as the author of the mentioned question:
I don't want to create a custom init like - (id) initCustom.
I don't want to use a flag to determine if the init has already been run once.
I also want to mention that I tried to create the UIBarButtonItem without using the initWithBarButtonSystemItem:target:action initializer - but I couldn't find a #property corresponding to the first parameter.
Thank you!
As mentioned in the link you have provided (see the accepted answer), if you initialise with super call, it will not result in recursive calls.
Probably you should have something like this:
- (id) init {
self = [super initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:#selector(myMessage)];
if (self) {
//Custom initialization
}
return self;
}
You may find this helpful: Concepts in Objective-C: Object Initialization
In particular, pay attention to the concept of the designated initializer:
The designated initializer plays an important role for a class. It ensures that inherited instance variables are initialized by invoking the designated initializer of the superclass. It is typically the init... method that has the most parameters and that does most of the initialization work, and it is the initializer that secondary initializers of the class invoke with messages to self.
When you define a subclass, you must be able to identify the designated initializer of the superclass and invoke it in your subclass’s designated initializer through a message to super. You must also make sure that inherited initializers are covered in some way. And you may provide as many convenience initializers as you deem necessary. When designing the initializers of your class, keep in mind that designated initializers are chained to each other through messages to super; whereas other initializers are chained to the designated initializer of their class through messages to self.
This is why you are seeing this behavior. initWithBarButtonSystemItem:target:action is the designated initializer for that class, and it in turn calls init. Overriding init to call a designated initializer won't for the very reasons you are describing.
Instead, you can create a convenience constructor that calls the designated initializer:
+ (instancetype) bookmarksBarButtonItemWithAction:(SEL)action {
return [[[self class] alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:action];
}
Which would achieve pretty much what you are looking for, and does not even require that you subclass UIBarButtonItem. It can be done as a category.
I'm still fairly new to Objective C so please bear with me:
My app has a Delegate, a Navigation Controller, and a View.
I also use a Singleton for "global" variables.
I know I can do this but it seems ungainly:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
-(void)myMethod{
GlobalData *globDat=[GlobalData getSingleton];
junk=globDat.someValue;
}
-(void)myOtherMethod{
GlobalData *globDat=[GlobalData getSingleton];
moreJunk=globDat.someOtherValue;
}
I'd like to this but it can't:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
GlobalData *globDat=[GlobalData getSingleton]; //Compiler won't allow this line
-(void)myMethod{
junk=globDat.someValue;
}
-(void)myOtherMethod{
moreJunk=globDat.someOtherValue;
}
However I can do this:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
GlobalData *globDat;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
globDat=[GlobalData getSingleton];
}
return self;
}
-(void)myMethod{
junk=globDat.someValue;
}
-(void)myOtherMethod{
moreJunk=globDat.someOtherValue;
}
So, is there a standard/common/proper "init" method to use for all Classes?
Should I just do this in every Class?
-(id)init{
if(self=[super init]){
globDat=[GlobalData getSingleton];
}
return self;
}
I know I can do this but it seems ungainly...
You seem to be asking how to do away with the part where you retrieve the value you want from your singleton. The best way to do that is to eliminate the singleton in the first place.
You say that you have an app delegate, a navigation controller, and a view. You probably also have a view controller. If those are the main objects in your application, you might consider storing your data in your view controller. Or, convert your singleton into a legitimate data model, and let your view controller keep a reference to it in a property. Then you can say something like:
-(void)myMethod{
junk = self.model.someValue;
}
which is pretty close to what you seem to be asking for.
It depends on how much you will use GlobalData.
extensive use in many methods
If you plan to make an extensive use of GlobalData in your class (in many methods), then, you should better add
#propertu (nonatomic, readwrite, weak)GlobalData *globalData in your .h
(or, better, in the class extension). In your main init... method, you set the value self.globalData = [GlobalData getSingleton];. You can also do as you did, with an instance variable, GlobalData* myGlobalData in your .h or in the class extension.
Make sure your init... method starts with init and make sure there is a "main" init... method (we call it the "designated initializer"). You can simply override the init method of NSObject. But, if you need, you can define
- (id)initWithString:(NSString*)str
{
self = [super init] ; // or another one ; here you should use the designated init. of the parent class
if (self){
self.string = str ;
self.globalData = [GlobalData getSingleton] ;
}
return self;
}
extensive use in few methods
If you intensively use GlobalData in one or two methods, you don't need to do so. You will just define a local variable GlobalData * globalData = [GlobalData getSingleton]; and use it.
The third code you propose is not objected-oriented.
I'm not 100% sure what you're question is really about (singletons as a common pattern vs common -init method?), but it is common to create what is called a designated initializer for your class. If you're not using a singleton pattern an init method signature that takes a value might look like this:
-(id)initWithValue:(NSString*)newValue;
As for using a singleton pattern to provide access to your variables I would recommend a more loosely coupled approach, especially if the number of values your singleton class manages begins to grow. I would put those variables into their own classes (separated logically) and use them as needed, by passing them to constructors, or instantiating them. That's an opinion/philosophy issue, though.
I ended up using this to resolve the question.
(Again I was primarily trying to clean out the redundancy of code)
In my Class modules I did the following:
#import "GlobalData.h"
GloablData *globDat;
-(id)init{
if(self=[super init]){
globDat=[GlobalData getSingleton];
}
return self;
}
...
-(void)someMethod{
int junk=globDat.junkvalue;
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
First off, I am using the game engine LevelHelper.
Does anyone know the proper way to access an instanced LevelHelper helper class from another class?
For example:
I have my main gameLayer class and a hudLayer class. The hudLayer class is being imported and instanced in the gameLayer class. However i need to be able to access and manipulate the sprites that are loaded in the hudLayer class with gameLayer class.
I was recommended to use the LevelHelper helper instance method. So i create the instance method inside of my hudLayer class, and then call it inside of my init method to load the sprites. I tried using this method as an instance method and i get an error saying unrecognized selector
+[hudLayer hudLoader];
If i try using the method as a class method i get an EXC_BAD_ACCESS Error.
I cant seem to find a solution.
My code:
hudLayer.h :
+(LevelHelperLoader*)hudLoader;
hudLayer.mm :
+(LevelHelperLoader*)hudLoader
{
LevelHelperLoader* lh;
finishScreen = [lh spriteWithUniqueName:#"finishScreen"];
return lh;
}
gameLayer.h :
LHSprite* finishScreen;
gameLayer.mm :
#import hudLayer.h
-(id) init {
[self retrieveRequiredObjects];
}
-(void) retrieveRequiredObjects {
finishScreen = [[hudLayer hudLoader] spriteWithUniqueName:#"finishScreen"];
NSAssert(finishScreen!=nil, #"Couldn't find the menu!");
}
Note: This code is just to make sure my logic and implementation of this is correct.
Any help is appreciated, thanks.
Have you tried stepping through your code in the debugger to find exactly which line causes the crash?
To me it looks as if it is here:
LevelHelperLoader* lh;
finishScreen = [lh spriteWithUniqueName:#"finishScreen"];
You have declared 1h, but you haven't created it. So you are sending a message to a non-existent object.
At very least, something like
LevelHelperLoader* lh = [[LevelHelperLoader alloc] init];
would help.
A cursory glance at the documentation adds more detail:
LevelHelperLoader* loader = [[LevelHelperLoader alloc] initWithContentOfFile:#"level1"];
In the docs, this is an instance variable - which suggests that hudLoader should be an instance method, not a class method:
- (LevelHelperLoader*) hudLoader;
and you should create your LevelHelperLoader* instance in your hudLoader initialiser.
update
You say in your comment:
inside of my init method for hudLayer.mm i call
lh = [[LevelHelperLoader alloc] initWithContentOfFile:#"level1"];
and in the .h i have
LevelHelperLoader* lh;
I am not sure if this is modifications since reading my answer or not. However here are some more thoughts.
Firstly can you sort out your naming conventions. Classes should start with Capitals.
HudLayer.h
Let's declare this lh instance variable as a property in your #interface and improve it's name:
#property (strong) LevelHelperLoader* levelHelper
HudLayer.mm
Allow it to be auto-synthesized or synthesize in your #implementation as:
#synthesize levelHelper = _levelHelper;
Then in your init method
_levelHelper = [[LevelHelperLoader alloc] initWithContentOfFile:#"level1"];
and hudLoader becomes
-(LevelHelperLoader*)hudLoader
{
finishScreen = [self.levelHelper spriteWithUniqueName:#"finishScreen"];
return self.levelHelper;
}
but then ask yourself, what is -hudLoader actually doing? The line that assigns to finishscreen? Is finishscreen an iVar? Do you need it? Perhaps not. Aside from that, all -hudLoader is doing is returning your already-created instance of LevelHelperLoader. Now that your iVar is a property you can access this from gameLayer using dot-notation property syntax, and remove hudLoader altogether:
GameLayer.h
#interface
#property (strong) Hudlayer* hudLayer;
#end
GameLayer.m
-(id) init {
_hudLayer = [[Hudlayer alloc] init];
[self retrieveRequiredObjects];
}
-(void) retrieveRequiredObjects {
finishScreen = [self.hudLayer.levelHelper spriteWithUniqueName:#"finishScreen"];
NSAssert(finishScreen!=nil, #"Couldn't find the menu!");
}
This makes me wonder whether you need a hudLayer class at all (maybe it is doing other useful work)... it looks as if you can get at your levelHelper directly from gameLayer.
GameLayer.h
#interface
#property (strong) LevelHelperLoader* levelHelper;
#end
GameLayer.m
-(id) init {
_levelHelper = [[LevelHelperLoader alloc] initWithContentOfFile:#"level1"];
[self retrieveRequiredObjects];
}
-(void) retrieveRequiredObjects {
finishScreen = [self.levelHelper spriteWithUniqueName:#"finishScreen"];
NSAssert(finishScreen!=nil, #"Couldn't find the menu!");
}
To conclude, I am not suggesting you follow this code line-for-line because I have no idea the broader context of your project. But you do need to sort out your confusion between classes and instances, allocation, instantiation, local vs instance variables. Please take care with naming conventions so that you know when you are sending a message to a Class or an instance of that class, and you know when you are addressing an iVar _directly or via a #property (eg self.property). Be consistent. And think about what a class is actually doing for you.
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.