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.
Related
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
}
}
Hy I have a problem accessing variables in another class.
Im making an app that makes note and let you study from the notes you make, for example the user makes a note that says "Oceanic dolphins: are members of the cetacean...", and when the user press a button to study it appears something like this "what are Oceanic Dolphins" then the user press a button it appears something like this "they are members of the cetacean..." the problem I have is this When i enter the ViewController that makes the question it appears empty I think the problem lies on one of the next codes
I make the Variable Globals like this
QueRes.h
#import <Foundation/Foundation.h>
#interface QueRes : NSObject
#property NSString *question;
#property NSString *response;
#end
QueRes.m
#import "QueRes.h"
#implementation QueRes
#end
I divide the NSString of the note like this
NSArray *card = [_argumentTextView.text componentsSeparatedByString:#":"];
QueRes *make = [[QueRes alloc] init];
if ([card count] >= 2)
{
make.question = card [0];
make.response = card [1];
}
the I apply the variable question and response in a ViewController like this
- (void)viewDidLoad
{
[super viewDidLoad];
QueRes *make = [[QueRes alloc] init];
_questionTextView.text = make.question;
}
then in other view controller i have the same code but apply with the response variable
Please help me I been stuck in this for weeks (I have Xcode 5 and the app runs in IOS 7)
(if you need more of the code of the program to help me fix it just tell me )
You are making a new instance of a QueRes in your viewDidLoad method. Unless the init method of QueRes sets its question and response properties to something, they will be uninitialized, which is why you are not seeing anything in your text view: there is nothing to show.
Naming the QueRes instance you make in the third code block you posted make does not make it the same instance as the instance in the viewDidLoad method, and it is not a global variable at all. It is a separate instance of QueRes.
Well I'm just confused when the lazy instantiation should be used.
I understand the basic concept of lazy instantiation though.
" I understand that all properties start out as nil in Objective-C and that sending a message to nil does nothing, therefore you must initialize using [[Class alloc] init]; before sending a message to a newly created property. "(Lazy instantiation in Objective-C/ iPhone development)
m.file:
#property (strong, nonatomic) NSMutableArray *cards;
- (NSMutableArray *)cards
{
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
} }
Well, what I really don't get is when I'm supposed to use this type of instantiation?
Mostly I see the code like this:
h.file:
#interface Card : NSObject
#property (strong, nonatomic) NSString *contents;
m.file:
if([card.contents isEqualToString:self.contents]){
score = 1;
}
*This might be a stupid question but I'm really confused. I'm new here, Thanks.
There is no reason to use Lazy Instantiation/Lazy Initialization if you find it confusing; simply initialize your instance variables/properties in the class init methods and don't worry about it.
As the object is created as a side-effect of calling the getter method, it's not immediately obvious that it is being created at all, so one alternative, which would also mean you can use the default compiler-generate getter method, is to explicitly check for it in addCard:
- (void)addCard:(Card *)card
atTop:(BOOL)atTop
{
if (!self.cards)
self.cards = [NSMutableArray new];
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
(and removing the user-supplied getter method)
However the net-effect is the same as the code you posted, with the exception that self.cards will return nil until addCard is called, however I doubt this will cause a problem.
When using dot notation to access your instance variables, you are calling your getter method for that given property. Therefore, by using dot notation and lazy instantiation, your getter will always assert that a property is not nil before you send it a message. Therefore, code such as
[self.cards insertObject:card atIndex:0];
will actually call the getter at self.cards; if you use dot notation on your objects and program the getters accordingly, you will always ensure that your instance variables are allocated and initialized, while simultaneously cleaning up your init method for code that is much more important.
Lazy instantiation is a common practice among Objective-C programmers; I suggest getting into the flow of the convention.
EDIT: thanks for Raphael mentioning this in a comment previously.
Lazy instantiation is a performance enhancement in certain types of scenarios. One example would be a class that has a very expensive user facing UI string.
If you create many of instances of that class but only a very small subset of those instances will be shown in your UI, you waste a lot of CPU resources creating a very expensive UI string that rarely will be used.
I have a MKPolyline subblass which I want to implement NSCoding, i.e.
#interface RSRoutePolyline : MKPolyline <NSCoding>
I asked a question on the best way to encode the c-array and got an excellent answer. However, there is no init method defined on MKPolyline, i.e. there is no other way to give it data other than its class method polylineWithPoints:points.
Is this code where my comment is ok?
- (void)encodeWithCoder:(NSCoder *)aCoder
{
MKMapPoint *points = self.points;
NSUInteger pointCount = self.pointCount;
NSData *pointData = [NSData dataWithBytes:points length:pointCount * sizeof(MKMapPoint)];
[aCoder encodeObject:pointData forKey:#"points"];
[aCoder encodeInteger:pointCount forKey:#"pointCount"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSData* pointData = [aDecoder decodeObjectForKey:#"points"];
NSUInteger pointCount = [aDecoder decodeIntegerForKey:#"pointCount"];
// Edit here from #ughoavgfhw's comment
MKMapPoint* points = (MKMapPoint*)[pointData bytes];
// Is this line ok?
self = (RSRoutePolyline*)[MKPolyline polylineWithPoints:points count:pointCount];
return self;
}
You should call an init method on any subclass of NSObject. Since MKPolyline is an NSObject, you should init it.
But MKPolyline has no methods and no init. This is Objective C's was of telling you that you can't subclass it.
Instead, as WDUK suggested, define your own class. It keeps track of your list point points, and manages NSCoding to save and restore them as needed.
#interface RSPolyline: NSObject<NSCoding>
- (id) initWithPoints: (NSArray*) points;
- (id) initWithCoder:(NSCoder *)aDecoder;
- (void) encodeWithCoder:(NSCoder *)aCoder;
- (MKPolyline*) polyLine;
#end
Your class can generate a polyline on request, perhaps caching the result if performance is an issue.
As a rule, don't reach for inheritance first. When you want to extend and improve a class, think first of composition.
It's dirty not to call [super init], and it doesn't bode well with my idea of good programming. Without calling super yourself, it isn't a true subclass; just a bastardization of composition that relies on a side effect of calling a convenience constructor. Saying this, I believe your method described will work OK, but it goes against the grain of good Objective-C programming and its conventions.
What I would suggest is to use MKPolyLine as an MKPolyLine instance, and use a category to add the extra bells and whistles you need. As for adding extra instance variables and such, you can use associated objects. An introduction to this concept can be found here, and this SO question addresses the use of them with categories: How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?
While it is generally allowed to create and return a different object in an init method, there are three problems with that line (explained below). Instead of this, I would suggest overriding the points and pointCount properties so that you can return values stored in an instance variable, and call the super implementation there if the instance variable is empty. Then, your initializer just sets these instance variables so that they will be used.
- (MKMapPoint *)points {
if(myPointsIvar == NULL) return [super points];
else return myPointsIvar;
}
// similarly for pointCount
The first problem is that you are creating a new object, but not releasing the old one, which means you are leaking it. You should store the result in a different variable, then release self, then return the result (you don't need to store it in self).
Second, polylineWithPoints:count: returns an autoreleased object, but initWithCoder: should return a retained one. Unless there is another retain on it, it could be deallocated while you are still using it.
If these were the only problems, you could solve both like this:
MKPolyline *result = [MKPolyline polylineWithPoints:points count:pointCount];
[self release];
return [result retain];
However, there is a third problem which cannot be solved so easily. polylineWithPoints:count: does not return a RSRoutePolyline object, and the object it returns may not be compatible with your subclass's methods (e.g. it probably won't support NSCoding). There really isn't a way to fix this, so you can't use polylineWithPoints:count:.
Could someone share some knowledge on whats best practice / code convention on using #property iVars in init methods or designated initializers?
please see my example:
#interface MyClass ()
#property(nonatomic,strong) nsstring *tempString;
#property(nonatomic,strong) NSMutableArray *arrItems;
#end
#implementation ViewController
- (id)init
{
if (self = [super init]) {
//Is this best practice / correct
_tempString = #"";
_arrItems = [[NSMutableArray alloc] initWithCapacity:0];
...
...
//Or this
self.tempString = #"";
self.arrItems = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
#end
Any advice on why one or the other should be used?
Thanks...
Apple's guidance on this topic is included in the aptly named section Don’t Use Accessor Methods in Initializer Methods and dealloc.
Read this thread: Why shouldn't I use Objective C 2.0 accessors in init/dealloc?
In other words if you are not goiung to use KVO you can use second approach:
//Or this
self.tempString = #"";
self.arrItems = [[NSMutableArray alloc] initWithCapacity:0];
But be care full with alloc-init, don't forget about autorelease.
It's typically better to use property notation when you define it, partly(mostly?) for the reason Jeremy mentioned.
Debugging a particular variable is a whole lot easier when you can set a breakpoint in method setter override and have it apply to ALL code paths that modify the variable.
Another reason is to keep a consistent memory management model, although it is less important since you are using ARC. If you weren't however, and strong was retain, then you would make sure that the object you are setting to the property is autoreleased everywhere you set the property, and not have to deal with releasing the current value if you are directly setting the variable.
Consistency is important for maintenance/readability and debugging, no matter what practices you use.
I prefer the lazy instantiation method for properties.
After you #synthesize you can override your getter to lazily instantiate your property
For Example:
-(NSString *)tempString {
if(!tempString) {
_tempString = #"";
}
return _tempString;
}
and
-(NSMutableArray *)arrItems {
if(!_arrItems) {
_arrItems = [[NSMutableArray alloc] initWithCapacity:0];
}
return _arrItems;
}
If you do want to set your property in the init method, use dot notation self.myProperty so that it uses the defined setter for the property and not the private class method directly.
According to Apple, you should not use accessors in init... or dealloc methods:
You should always access the instance variables directly from within
an initialization method because at the time a property is set, the
rest of the object may not yet be completely initialized. Even if you
don’t provide custom accessor methods or know of any side effects from
within your own class, a future subclass may very well override the
behavior.
Taken from this doc: Encapsulating Data.