Instance method unable to read class instance variables - iphone

Problem: Variables from a class are being overlooked somehow when the method is called from a different class.
Basically: I have Class A, Class B. I also have Method A, Method B each in their respective classes.
When calling Method A from Class B Method B, I can NSLog values fine, however I cannot access an NSMutableArray contained within Class A. There is my issue.
// Class B
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
foodListingObj = [[FoodListing alloc] initWithNibName:#"FoodListing" bundle:nil];
}
return self;
}
- (void)toggleImage //Method B in Class B {
[foodListingObj didToggle:self.indexOfToggledCell];
}
// Method in Class A
- (void)didToggle:(NSIndexPath *)toggledIndexPath{
//[_toggledIndexArray addObject:toggledIndexPath];
[_toggledIndexArray addObject:#"Anything"];
}
// Method in Class A
- (void)checkArray{
// Log the count of the array
// it always says 1 because I intialize the array
// then add an an object like so [_toggledIndexArray addObject#"hi"];
// in my ViewDidLoad Method. Hence it appears that the array is still around
// (not deallocated), but yet my method cannot seem to touch it...
NSLog(#"%i",[_toggledIndexArray count]);
}
// dealloc for Class A
- (void)dealloc{
// I release the array among other things
[_toggledIndexArray release];
}
The array (_toggledIndexArray) is a property declared in the header, and is initialized in Class A's viewDidLoad with
_toggledIndexArray = [[NSMutableArray alloc] init];
The error is the fact that Method A does not seem to affect the array for a reason unknown to me.

From the code above, it looks like your problem is that you are creating a new instance of FoodListing each time your toggleImage method is called.
Is this really what you intend to do? If it is, ViewDidLoad isn't going to get called in this case, since you are just allocing and initing the view controller as opposed to using initWithNibName, so unless you are doing something in your custom loadView method, there isn't going to be a view that is loaded.
However, I don't expect you do want to create a new instance each time, the implication from your code is that the FoodListing object already exists and has the array populated already.
So, in whatever "class b" is, declare a property of type FoodListing. Set this to your FoodListing view controller (this will be either when class b is created or presented, or when the FoodListing object is first created, you havent given enough context for me to say). Call the methods on the object held in the property instead of creating a new one.

You need to maintain the FoodListing object in Class B as class variable and not always alloc/init it in toggleImage function. Perhaps do
if(self.foodListing==nil) { //alloc/init it}else{ //do your thing here.}

Related

Making Xcode complain about a missing parameter

I am designing a new application by modernizing code I wrote in the past. This old code uses the class/delegate model and I am trying to transform them to use blocks as callbacks, not the delegate stuff.
What I do is to create a property like
#property (nonatomic, copy) void (^onTouch)(NSInteger index);
That would pass to the object using that class a block where code can be inserted and in this case executed on touch.
But my problem is this. When you use delegates and you have a method on the delegate protocol, Xcode will warn if you use that class and forget to implement the delegate protocols. Is that a way to do that with blocks? Or in other words: is there a way to make Xcode complain if a callback block is not defined by the caller?
I mean this would be the correct:
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = ^(NSInteger *index){ //call back code to be executed };
This would be OK too
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = nil;
but this would generate a message
MyClass *obj = [[MyClass alloc] init];
// no callback block defined.
Is this possible?
If you want to enforce setting a certain parameter, I would include it in the initializer.
MyClass *obj = [[MyClass alloc] initWithBlock:^(NSInteger *index) { /* code*/ }];
Then, in MyClass:
- (id)init {
// This will result in a runtime error if you use the wrong initializer.
NSAssert(NO, #"Use initWithBlock instead.");
}
- (id)initWithBlock(initWithBlock:^(NSInteger *)block) {
self = [super init];
if (self) {
self.onTouch = block;
}
return self;
}
Also note, attempting to execute a NULL block results in a crash, so make sure to do:
if (self.onTouch) { self.onTouch(); }
Wherever you run the block.
First, I strongly recommend defining types to represent your blocks - makes them a lot easier to work with, especially if you need to refactor the parameters.
You can't write code that distinguishes between "I set this property to nil" or "the runtime initialized this property to nil", at least not without some crazy runtime code to check the stack. Only option I can think of would be to use the null object pattern. Before I elaborate, bear in mind that I haven't actually tried to test this, but it should work. Define a block that means 'has no value' and set your property to point to that block on init. Then you can compare to that NullBlock at runtime to identify if someone explicitly set the property to nil (because it would be nil at that point) or gave it a real non-nil value.
Alternatively, if you don't mind manually writing your set accessors, you could have a BOOL that tracks if someone set the property explicitly. Then when you call the block just check if someone actually set the value or not.
#synthesize onTouchBlock=_onTouchBlock;
MyBlock _onTouchBlock;
BOOL _onTouchBlockWasSet;
- (void)setOnTouchBlock:(MyBlock)block {
_onTouchBlockWasSet = YES;
_onTouchBlock = block;
}
I would not recommend passing the value in the initializer because that makes it tied to the creation of that object type. If you wanted to change the block in code based on some condition, you'd be back to square one. Also, it prevents you from using storyboards which create that object.

Objective-c Is super init a class method or instance method?

I am new in objective c. I understand that -init() is an instance method and return an object
e.g. myObj=[myObj init]; will return an object myObj.
However, if self =[super init]; normally super refer to parent class e.g. NSObject which is a class, not instance.
So, Is -init() instance method or class method for super init?
thanks
init is an instance method. The fact that you call it on super does not change it.
Keep in mind that super does not represent the class of your object, but your object seen as an instance of its parent class in the class hierarchy.
And you never call myObj=[myObj init]; — you call myObj = [[MyObj alloc] init]. Notice the case difference between myObj (a variable) and MyObj (the class of which this variable is an instance).
Generally init is used after alloc in this way:
MyObject* obj = [[MyObject alloc] init];
and alloc create the object instance, so init is an instance method, and when you override it, it's good habit to call always the parent class init.
Try to read this article.
Super is referring to the methods of the parent class in the current object. Calling super init will call the method init on the current object (self), but will use the implementation of the super class. So no - init is not a static method (this would be visible due to a + before the init method). you can call super whatMethodYouWantFromSuperclass even though it is not static. Static methods are not called on an object (self) but on a class ([NSObject yourStaticMethod]).

Obj-C: Passing pointers to initialized classes in other classes

I initialized a class in my singleton called DataModel. Now, from my UIViewController, when I click a button, I have a method that is trying to access that class so that I may add an object to one of its dictionaries. My get/set method passes back the pointer to the class from my singleton, but when I am back in my UIViewController, the class passed back doesn't respond to methods. It's like it's just not there. I think it has something to do with the difference in passing pointers around classes or something. I even tried using the copy method to throw a copy back, but no luck.
UIViewController:
ApplicationSingleton *applicationSingleton = [[ApplicationSingleton alloc] init];
DataModel *dataModel = [applicationSingleton getDataModel];
[dataModel retrieveDataCategory:dataCategory];
Singleton:
ApplicationSingleton *m_instance;
DataModel *m_dataModel;
- (id) init {
NSLog(#"ApplicationSingleton.m initialized.");
self = [super init];
if(self != nil) {
if(m_instance != nil) {
return m_instance;
}
NSLog(#"Initializing the application singleton.");
m_instance = self;
m_dataModel = [[DataModel alloc] init];
}
NSLog(#"ApplicationSingleton init method returning.");
return m_instance;
}
-(DataModel *)getDataModel {
DataModel *dataModel_COPY = [m_dataModel copy];
return dataModel_COPY;
}
For the getDataModel method, I also tried this:
-(DataModel *)getDataModel {
return m_dataModel;
}
In my DataModel retrieveDataCategory method, I couldn't get anything to work. I even just tried putting a NSLog in there but it never would come onto the console.
Any ideas?
Most likely you are sending messages that get ignored, e.g. they're being sent to objects which don't exist/aren't the one you're looking for, and for some reason aren't crashing. This occurs in the case of messaging nil, or possibly other illegitimate values. Although you seem to expect that the m_ variables will be initialized to 0, this is not good form, and furthermore you are not following a very typical objc pattern for your singletons -- m_dataModel should be an ivar of m_instance, and m_instance should probably be declared static, as you probably don't want it accessed from other files directly. In addition, the most likely source of your bug is somehow the -init method, which should never be called on a singleton -- instead do something like this:
+ (ApplicationSingleton *)sharedInstance {
static ApplicationSingleton *instance = nil;
if(!instance) {
instance = [[self alloc] init]; //or whatever custom initializer you would like, furthermore some people just put the initialization code here and leave -init empty
}
return instance;
}
the code you have now leaks because you allocate an object (self) and don't release it before returning a potentially different instance (the shared one if one already exists), such that the newly allocated one is typically lost.

Change classes instantiated with loadNibNamed

I am trying to change the class of objects created with a nib with the iPhone SDK.
The reason for this is; i dont know until runtime what the class is that i want the nib object to be (though they will have the same UIView based super class), and i dont want to create a different nib for every eventuality - as the .nib will be the same for each, apart from the class of one object.
I have been successful, with a couple of methods, but either have some knock on effects or am unsure of how safe the methods I have used are:
Method 1: Override alloc, on the super class and set a c variable to the class I require:
+ (id) alloc {
if (theClassIWant) {
id object = [theClassIWant allocWithZone:NSDefaultMallocZone()];
theClassIWant = nil;
return object;
}
return [BaseClass allocWithZone:NSDefaultMallocZone()];
}
this works well, and i assume is 'reasonably' safe, though if have a nib with the correct class as the class identity in the Nib, or I alloc a subclass myself (without setting 'theClassIWant') - an object of the base class is created. I also dont really like the idea of overriding alloc...
Method 2: use object_setClass(self,theClassIWant) in initWithCoder (before calling initWithCoder on the super class):
- (id) initWithCoder:(NSCoder *)aDecoder {
if (theClassIWant) {
// the framework doesn't like this:
//[self release];
//self = [theClassIWant alloc];
// whoa now!
object_setClass(self,theClassIWant);
theClassIWant = nil;
return [self initWithCoder:aDecoder];
}
if (self = [super initWithCoder:aDecoder]) {
...
this also works well, but not all the subclasses are necessarily going to be the same size as the super class, so this could be very unsafe! To combat this i tried releasing and re-allocing to the correct type within initWithCoder, but i got the following error from the framework:
"This coder requires that replaced objects be returned from initWithCoder:"
dont quite get what this means! i am replacing an object in initWithCoder...
Any comments on the validity of these methods, or suggestions of improvements or alternatives welcome!
While I'm curious to see if you can pull this off using your approach, you may want to consider using custom placeholder objects.

Trouble Copying custom class initialization

I have a custom class of type NSObject that contains a single NSMutableArray. This class is called Mutable2DArray and is designed to emulate a 2 dimensional array of type NSMutableArray. There is a custom init method - (id)initWithX:(int)x Y:(int)y that asks for the dimensions for the array and allocates the required arrays within the only array the class owns.
My issue is when I try to copy an instance of Mutable2DArray I get an error saying the copyWithZone is an unrecognized selector. I thought copy was a base method of NSObject so I'm confused why I cant create a copy of the instance like this:
Mutable2DArray *Array1 = [[Mutable2DArray alloc] initWithX:10 Y:10];
Mutable2DArray *Array2 = [Array1 copy];
Am I missing something so obvious here?
Things that I can think of to check, off the top of my head:
Does the header file actually declare the interface as inheriting from NSObject?
Does your custom initWithX: Y: method call [super init] before finishing?