Issues with including Objective-C class in my xcode project - iphone

I am relatively new to Objective-C, about 1 year experience, and I had encountered an issue with trying to add a class to my project. When I add a class of UIViewController subclass, with XIB file included, I have no problems with that at all, xcode is working very well that way.
However, I tried to add a simple Objective-C class to the project called Test, with the following .h and .m files, and had a problem where the code compiles and builds without error but the method TestMethod always returns nil. What might be wrong here?
Test.h
#import <Foundation/Foundation.h>
#class Test;
#interface Test : NSObject {
}
- (NSString *)TestMethod;
#end
Test.m
#import "Test.h"
#implementation Test
- (NSString*)TestMethod {
return #"Test";
}
#end
In my UIViewController subclass with XIB file, that subclass works without error, but when I try to include my Test class in it, the method TestMethod returns nothing, even though it is hardcoded to always return the same string:
#import "Test.h"
Test *testobject;
// this compiles and builds but returns nothing
NSString *testString = [testobject TestMethod];

You missed to alloc + init.
Use
Test *testobject=[[Test alloc] init];
or
Test *testobject=[Test new];
Whenever your object is un-initialised you will get nil value.
EDIT:
In ARC : it's default initialized .
In MRC : the value could be uninitialized (garbage value).

TestMethod isn't returing nil - testobject is nil.
Change
Test *testobject;
to
Test *testobject = [[Test alloc] init];

You have not created an instance of Test, so testObject just holds nil. You need to assign an instance of Test to the variable in order to do what you want.

You can also take this approach
//Test.h
#import <Foundation/Foundation.h>
#class Test;
#interface Test : NSObject {
}
- (id)init;
-(NSString*)TestMethod;
#end
Now in your Test.m file
//Test.m
#import "Test.h"
#implementation Test
- (id)init {
if (self=[super init]) {
}
return self;
}
-(NSString*)TestMethod {
return #"Test";
}
#end
Now if you want to call this Test Class in another class, you have to create an instance of Test Class.
Test *testobject = [[Test alloc] init];
NSString *testString = [testobject TestMethod];

To access any method/property of a class, first you need to allocate memory to object of that class using alloc/new method.
Since you created variable of that class type <Test *testobject>. But variable does not allocated any memory, by default it will be nil. Using "nil" you can call any method in objective C. It will not crash. But it will return nil.
So, Before accessing any object you must created memory for that object
Test *testobject = [Test alloc];
initialized the object with default constructor (init, initWith, etc...)
[testobject init];
Now object is ready for calling instance method/setter/getter etc...
NSString *testString = [testobject TestMethod];

Related

Can't access class instance from another class

in my AppDelegate I have imported the header of a class I have created and propertied and syntesized an instance of it (in AppDelegate). Now I'm trying to access a method and a variable inside this instance from two other views. I'm relatively new to objective-c, but so far I've learned that if I do this:
AppDelegate *appdelegate = [AppDelegate new];
I will just get a fresh instance from the class inside AppDelegate, so if I set a variable from one view, I can't access it from the other. I've read that if I would do it this way:
AppDelegate *ap = (AppDelegate *)[[UIApplication sharedApplication] delegate];
It would allow me to access the existing instance. But it doesn't work.
Is the way I'm trying to do this totally wrong? Thanks a lot for your help!
UPDATE:
When I do this inside AppDelegate:
myClass.string = #"test";
NSLog(#"appDelegate: %#", myClass.string);
I get this:
appDelegate: (null)
UPDATE2:
I wrote #class AppDelegate; underneath the #import lines in the viewController, but still I can't access myClass. A main problem, which may be the cause why this isn't working from the views, is that I can't even access myClass from AppDelegate.
In AppDelegate.h I wrote:
#property (strong, nonatomic) testClass *myClass;
In AppDelegate.m:
#import "testClass.h"
#synthesize myClass;
This should be right, right?
myClass.h
#property (strong, nonatomic) NSString *string;
myClass.m
#synthesize string;
When I then try to access myClass from appDelegate, I write:
self.myClass.string = #"test";
NSLog(#"appDelegate: %#", self.myClass.string);
The result is:
appDelegate: (null)
I think you have to allocate and initialize the myClass
Write myClass = [MyClass alloc] init] in AppDelegate.m file
#import "AppDelegate"
and also write
#class AppDelegate;
Unless you haven't shown it, you're not allocating testClass and not assigning it to myClass. Objective-C is not like C++ or Java where you can simply declare a variable of a particular class type and have it instantiated on the stack. Each class you use must be instantiated, whether manually or through InterfaceBuilder. The exception is there are some classes provided by the various frameworks which have a single shared instance. Rather than allocating those classes, you simply ask for the shared instance. However, that's not the case here. It's your own class, so you need to allocate it.
It would look like:
myClass = [[testClass alloc] init];

Using Custom Subclass of NS Object in 2 different IBActions

This is an objective-c problem. I have created a subclass person of NSObject with parameters 'height' and 'weight', with property and synthesize in a file called Person.h that contains both interface and implementation.
I want to import Person.h into my viewcontroller.m and create person objects and alter them using 2 IBActions.
-(IBAction)alterperson_1{
person *bob = [person alloc]init];
bob.height = 72;
bob.weight = 200;
}
-(IBAction)alterperson_2{
bob.height = 80;
bob.weight = 250;
}
This arrangement does not work because the method alterperson_2 can't find Bob because it is a local variable to alterperson_1. My question is how and where in viewcontroller.m do I allocate Bob as a person so that his attributes can be altered by both IBActions.
I have tried allocing in viewdidload as well as in the initwith nibname methods. It did not work. I have also tried in the implementation{ } of viewcontroller.m but that doesn't work either because Bob's allocation is not a compile time constant.
Thanks!
Update With Code
So, I have the Person.h file importing properly now (thanks Robotnik), and am able to create instances of Person throughout ViewController.m -- however, my created instance *bob does not seem to retain values for its properties (see comments by NSLog statements in the code). I think this is an initialization issue, but I have no idea where to initialize. Currently, I get a warning when initializing in viewDidLoad. How do I get bob.weight to print 200 when my IBAction is called, instead of the 0 I currently get? Thanks.
// Person.h
#import <Foundation/Foundation.h>
#interface Person : NSObject{
int weight;
int height;
}
#property int weight, height;
#end
end Person.h
//Person.m
#import "Person.h"
#implementation Person
#synthesize weight, height;
#end
end Person.m
//ViewController.h
#import <UIKit/UIKit.h>
#import "person.h"
#interface ViewController : UIViewController{
}
#property Person *bob;
-(IBAction)persontest:(id)sender;
#end
end ViewController.h
//ViewController.m
#import "ViewController.h"
#implementation ViewController
#synthesize bob;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Person *bob = [[Person alloc]init]; // this causes a local declaration warning, if I remove this code, however, it still doesn't work
bob.weight = 100;
NSLog(#"viewDidLoad bob's weight, %i", bob.weight); // this will print 100, but only because I made the local initialization. The value is lost once the viewDidLoad Method ends.
}
-(IBAction)persontest:(id)sender{
bob.weight = bob.weight + 100;
NSLog(#"IBAction bob's weight %i", bob.weight); // this prints 0, probably because value is nil. How can I make it print 200?
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
end ViewController.m
You'll need to declare Bob in your ViewController.h if you want him to be accessible across multiple methods. You can then initialise him in viewDidLoad
#interface ViewController
{
Person *bob;
}
-(IBAction)alterperson_1;
-(IBAction)alterperson_2;
#end
You mentioned you wanted to instantiate multiple people. In that case you may want to keep multiple Person objects in an NSMutableArray or similar. This should still be declared in the ViewController.h in order to be accessible in multiple methods. You can then use the method addObject: to add people to the array. Here is an example of an array, storing strings.
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
[stringArray addObject:#"Dog"];
[stringArray addObject:#"Cat"];
You seem to be confusing declaration of a variable with allocation of a variable. You're also not using properties properly.
Declaration of a variable is done like this:
Person *bob;
That's it. If you put this line in your #interface section, or in {braces} at the top of your implementation section, then this becomes an instance variable, any time you use bob in the rest of your class it will know that you are talking to a person. So, in your viewDidLoad, you can now do this:
bob = [[Person alloc] init];
And it knows you are referring to the instance variable. In your current code, you have Person * in front of this line, which, as the compiler is telling you, is declaring a local variable with the name bob, and therefore hiding your instance variable, so you are not changing your instance variable at all, so there is no value in it later.
Looking at the nature of this question, and your comments, I would strongly advise reading some objective-c introductory texts before proceeding much further. You can ask questions here but SO is not really the place to learn a language - most answerers will assume that you know the language basics.

Category-like extension for instance variables

Is there a way to somehow emulate category behavior for a class regarding to it's instance variables, not methods ?
I have a ClassA, and I want to keep its name after extending it with new methods AND ivars from other cllass (ClassB).
Of course, I can inherit ClassA, but resulting class will have different name.
For methods addition, it's not a problem - category would be a good solution.
UPDATE: ClassA used as file owner for a XIB, and these fields to be extended are IBOutlets. So I need them at build phase.
Since the iPhone uses the modern Objective-C runtime, you can use associative references to add data to instances without having to declare instance variables. See the documentation for objc_setAssociatedObject etc.
If you wrap the calls to the runtime in standard accessor methods, it will be very easy to use.
I've investigated this question playing around associative references (thanks to Ole), with methods static variables, methods swizzling, and finally come to this simple solution (no runtime stuff). I simply use "categorized" class only to return a pointer to a derived class, which of course can contain additional ivars. Doing so I achieve one unexpected benefit: I can call super's class methods, which is impossible when extending through categories.
Example of a class extension (tested):
ClassA+ClassB.h
#protocol _ClassB_Protocol
#optional // to avoid warnings
- (IBAction) onClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *aButton;
#end
#interface ClassA (_ClassA_Category) <_ClassB_Protocol>
#end
#interface ClassB: ClassA <_ClassB_Protocol> {
UIButton *aButton; // _ivar_ to add
}
#end
ClassA+ClassB.m
#implementation ClassA (_ClassA_Category)
// this will be called first on [ClassA alloc] or [ClassA allocWithZone:(NSZone *)zone]
+(id) alloc {
if ([self isEqual: [ClassA class]]) {
return [ClassB alloc];
} else {
return [super alloc];
}
}
#end
#implementation ClassB: ClassA
#synthesize aButton;
-(void) dealloc {
[aButton release];
[super dealloc]; // this is impossible for an ordinary category
}
- (void) onClick:(id)sender {
// some code here
}
#end
Now we have in the same time:
ClassB "extends" ClassA (category way);
ClassB inherits ClassA (ClassB can call ClassA methods);
ClassB can be accessed through ClassA name (category way)
I put Martin's example into a trivial app replacing ClassA with NSData, ClassB with XXData, and onClick with getIvar, and invoked it (Mac OS X 10.6.6, Xcode 4 Final) with:
NSData * data = [NSData data];
NSLog(#"%#", [data getIvar]);
It fails with "-[NSConcreteData getIvar]: unrecognized selector sent to instance" ..
It fails because "alloc" in the NSData category (which returns the pointer to the derived class) is not called by the above code. If, instead, "alloc" is called explicitly, as in:
NSData * data = [[NSData alloc] init];
NSLog(#"%#", [data getIvar]);
then all is well.

instance method of class for iphone

i want to create some header file for future use but i have 1 problem
i have defined a method in lets say Rimage class called check1
now i want to call that from maiviewcont
so i did this
in mainVC.h
i defined a instance of Rimage class
#import <UIKit/UIKit.h>
#class Rimage;
#interface Rahul_imageplaceCordinatesViewController : UIViewController {
Rimage *aRimage;
}
#property (nonatomic,copy) Rimage *aRimage;
#end
and in .m
[self.aRimage check1];
aRimage = [Rimage check1];
but both are not working
i went for both +(void)check1 and -(void)check1 in Rimage class
The two examples you give do very different things:
[self.aRimage check1];
invokes an check1 instance method on aRimage.
aRimage = [Rimage check1];
calls the check1 class method on the Rimage class, and assigns the result to aRimage.
In both cases, you will need to #import "Rimage.h in your .m file, else you'll get warnings like "aRimage may not respond to 'check1'".
EDIT
Your ".h" file is declaring a property named "aRimage", but that value will be nil until you assign something to it, by doing something like:
self.aRimage = [[[Rimage alloc] init] autorelease];
See this question for a good explanation of the difference between class methods and instance methods.
ps. and don't forget to do a [aRimage release] in your dealloc method

iPhone dev - issue with #selector

in my app I am trying to run some code that currently exists in my applicationWillTerminate in appDelegate.
I have c/p'd the same code into the method that is currently running (verified by NSLog), but the code just doesnt seem to execute the same way.
The following code is from my applicationWillTerminate, which saves data, ready for loading next time.
[myArray makeObjectsPerformSelector:#selector(saveAllDataLeads)];
when I insert this into my DetailViewController.m (in a method that is currently active), I insert the following.
[appDelegate.myArray makeObjectsPerformSelector:#selector(saveAllDataLeads)];
The problem is that it just doesn't do the stuff in saveAllDataLeads, can someone see what is wrong? or is more information required.
Regards
in DetailViewController.h i have declared
MyAppDelegate *appDelegate;
The objects that you have added to myArray must have a selector with no parameters, named saveAllDataLeads, that is:
#interface MyObject : NSObject {
}
- (void)saveAllDataLeads;
#end
#implementation MyObject
- (void)saveAllDataLeads {
// do something
}
#end
Then, presumably somewhere you are adding instances of MyObject to myArray:
MyObject* instance = [MyObject new];
[appDelegate.myArray addObject:instance];
[instance release];