how to pass a variable between two viewcontroller? - cocoa-touch

i want to pass a variable from a viewcontroller to another the way i do is
at the first viewcontroller header file i declared a variable
1.h:
NSString *string;
and at the second viewcontroller i have import the 1.h in my 2.m file,the way i call the variable is
2.m:
NSString *string2 = 1.string
however it return an error can somebody teach me how to do,because of i dont have the strong basic at Object Oriented programming ,thanks

Just defining and declaring the two strings is not enough. That makes sure that each class has a variable called string or string2 - but when your program is running, it is actual objects that must refer to specific instances of string1 (or string2).
It's like designing a house with a letterbox - the letterbox is there on the plan of the house, but nothing happens until a specific letter gets sent to a specific house.
What you need to do is wire up the actual instances of your class, possibily in an init method, like this:
// 1.h
#interface ViewController1 : UIViewController
{
// declare our variable
NSString* string1;
}
// declare 'string1' as a property
#property (retain) NSString* string1;
// 1.m
// implements the property for string1
#synthesize string1;
// 2.h
#interface ViewController2 : UIViewController
{
// declare our variable
NSString* string2;
}
// declare 'string2' as a property
#property (retain) NSString* string2;
// 2.m
- (id)initWithTitle:(NSString*)aTitle andString1:aString
{
if (self = [super init])
{
self.title = aTitle;
self.string1 = aString;
}
return self;
}
Then in 1.m, you create the second controller, and wire the strings up, like this:
// 1.m
mySecondController = [[ViewController2 alloc] initWithTitle:#"Controller 2" andString:string1];

Although it is possible to directly access members variables like that (using the -> operator) it is not recommended.
The correct way is to provide an accessor to get/set your member variables.
In Objective-C 2.0 (iPhone and OSX 10.5) you can easily do this using the "property" keyword. As part of the property syntax you can also express how you wish "set" objects to be treated.
retained - the previous object will be released and the new one retained
copy - the object will be copied
assign - the object will be assigned.
These are the basics, I suggest you read up more about properties.
The below shows how you would use properties in your example. Note that because we are dealing with an NSString, which is an NSObject derived class, we use the "retain" option to ensure the reference counts are correctly updated.
// 1.h
#interface ViewController1 : UIViewController
{
// declare our variable
NSString* _string;
}
// declare 'string' as a property
#property (retain) NSString* string;
// 1.m
// implements the property for string
#synthesize string = _string;
// constructor for ViewController1
-(id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle {
if (self = [super initWithNibName: name bundle: bundle]) {
// Initialize the string here.
self.string = #"Hello World";
}
}
// 2.m
NSString* oldString = view.string;
view.string = #"New String";

Related

I do not understand ways of declaring instance variable in the code

I do not quite understand the way of declaring instance variable and property. Can someone explain in detail the difference of the two codes below? In the second method, if I use _name for instance variable, is it the same function as the way declaring name in first code? Thanks!
First Code:
// OrderItem.h
#import <Foundation/Foundation.h>
#interface OrderItem : NSObject
{
#public NSString *name;
}
-(id) initWithItemName: (NSString *) itemName;
#end
// OrderItem.m
#import "OrderItem.h"
#implementation OrderItem
-(id) initWithItemName: (NSString *) itemName {
self = [super init];
if (self) {
name = itemName;
NSLog(#"Initializing OrderItem");
}
return self;
}
#end
Second Code:
// OrderItem.h
#import <Foundation/Foundation.h>
#interface OrderItem : NSObject
#property (strong,nonatomic) NSString *name;
-(id) initWithItemName: (NSString *) itemName;
#end
// OrderItem.m
#import "OrderItem.h"
#implementation OrderItem
-(id) initWithItemName: (NSString *) itemName {
self = [super init];
if (self) {
_name = itemName;
NSLog(#"Initializing OrderItem");
}
return self;
}
#end
In the first case you have declared an instance variable (usually called an ivar in Objective-C).
In the second case you have declared a property. A property is a set of two methods, a getter and a setter, usually accessed using dot notation, e.g. self.name. However, an ivar is automatically synthesized for the property with the name _name. That instance variable is what you are accessing in your init.
You can actually change the name of the ivar using #synthesize name = _myName or not have it at all (if you declare the getter and setter manually, no ivar will be synthesized).
Objective-C properties are a rather complicated topic so don't worry if you don't understand it immediately.
Properties are public which means that other classes can read and write them (even classes that aren't subclasses of the class that declares the property). In addition to that, properties provide a getter and a setter method (mutator methods). The getter of a property gets called every time you access the property
NSString *aName = self.name;
Whereas the setter is accessed every time you write or assign to a property.
self.name = #"Some name";
Instance variables (or ivars) are, by default, only visible for the class that declares it and its subclasses (also known as being encapsulated by their class). You can change this default behavior when you add the keyword #public to your ivar declaration though.

Objective-c readonly copy properties and ivars

I'm try to grok properties declared as both copy and readonly in objective-c, and specifically, whether I have to do the copy myself. In my init methods. Evidence suggests I do:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData *test;
- (instancetype)initWithData:(NSData *)data;
#end
#implementation A
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
_test = data;
}
return self;
}
#end
int main (void) {
NSData *d1 = [NSMutableData dataWithBytes:"1234" length:5];
A *a = [[A alloc] initWithData:d1];
NSLog(#"%lx", (unsigned long)d1);
NSLog(#"%lx", (unsigned long)a.test);
return 0;
}
I had thought I could do self.test = data in my init method, but that is not permitted because it's readonly (not unexpectedly). Of course, self.test = [data copy] ensures two different objects.
So: Is there a way to create a readonly property in objective-c that copies the incoming value, or is it sufficiently an edge case that the combination is pointless and I have to do any copying myself manually anyway?
A #property declaration is merely shorthand for some accessor/mutator method declarations, and (in some cases) synthesized implementations for said accessor/mutator methods.
In your case, the #property(nonatomic, copy, readonly) NSData *test declaration expands to this equivalent code:
#interface A : NSObject
{
NSData* _test;
}
- (NSData*)test;
#end
#implementation A
- (NSData*)test
{
return _test;
}
#end
There is no setTest: mutator method because the property is declared as readonly, so the copy attribute has no effect.
You can implement your own mutator method:
- (void)setTest:(NSData*)newValue
{
_test = [newValue copy];
}
Or, you can have the compiler synthesize a mutator method for you by declaring a read/write property in a private class extension in your implementation file:
// A.m:
#interface A()
#property (nonatomic, copy) NSData* test;
#end
Both cases would allow you to use the test mutator method to copy a value to the _test instance variable:
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
The end result is:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData* test;
- (instancetype)initWithData:(NSData*)data;
#end
#interface A()
#property (nonatomic, copy) NSData* test;
#end
#implementation A
- (instancetype)initWithData:(NSData*)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
#end
In addition to what Darren said, the copy attribute describes what semantics the properties setter has. In your initializer, you're not using the setter, you're directly assigning to the instance variable.
It's maybe a bit hard to grok, but the instance variable is not the same thing as the property. It is used to implement the property in this case. But, assigning to the instance variable is not the same as setting the property.
If you want your initializer to also have the semantics that it copies the passed-in data, that's a separate design decision (although a good idea to go with the property's semantics). You could implement that by using a private setter as Darren suggests, but you could also just do:
_test = [data copy];
in the initializer.

Issue passing variable across class files

I am having difficulty passing variable from one class file to another. I have tried the Global Variable creator extern, but that also doesn't work.
Specifically, I am trying to pass a variable from a new class file I created called NDSClassSiteViewController to the master controller NDSClassMasterViewController.
I have added the #property into the .h file as follows:
#interface NDSClassSiteViewController : UITableViewController {
NSArray *sitesMenu;
}
#property (nonatomic, retain) NSString *siteid;
#end
And then the #synthesize into the .m file as follows:
#import "NDSClassSiteViewController.h"
#import "NDSClassMasterViewController.h"
#interface NDSClassSiteViewController () {
NSMutableArray *_objects;
}
#end
#implementation NDSClassSiteViewController
#synthesize siteid;
I set the variable in the .m file as follows:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showMenu"]) {
NSInteger row = [[self tableView].indexPathForSelectedRow row];
//NSArray *appcell = [sitesMenu objectForKey:#"Table"];
NSLog(#"AppCell %#", sitesMenu);
NSDictionary *entry = [sitesMenu objectAtIndex:row];
self.siteid = [entry objectForKey:#"SITEID"];
NSLog(#" sample SiteView %#", siteid);
//NDSClassMasterViewController *detailController = segue.destinationViewController;
//detailController.detailItemTwo = siteid;
}
}
Then I try and access it in the Master View controller using the following:
- (void)viewDidLoad
{
[super viewDidLoad];
globalid = siteid;
}
But I get an error: use of undeclared identifier siteid
I don't really know why it won't allow me to use the variable? Am I missing something?
You've declared a property on NDSClassSiteViewController. To access the property you need an instance of an NDSClassSiteViewController within scope, and you also must access it using the syntax for properties - either dot syntax or standard method call syntax.
To read the property:
aSiteViewController.siteID
[aSiteViewController siteID]
To set it:
aSiteViewController.siteID = #"Site ID here";
[aSiteViewController setSiteID:#"Site ID Here"]
When you are in a method of NDSClassSiteViewController then self points to the instance you likely want to use. Otherwise, you'll probably either create it in the method or have it passed in as a parameter. Also, when you are in an instance method of NDSClassSiteViewController you can access the instance variable that backs the property directly - usually this would be _siteID but you manually synthesized so just siteID should work. Note that I did siteID instead of the siteid you're using. I did this for readability and to follow established Objective-C naming conventions.

Properties and accessors in Objective-C

Does the following code call an accessor "set" function or does it modify the pointer myMember directly?
aClass.h
#interface MyClass : NSObject {
NSArray *myMember;
}
#property (nonatomic, retain) NSArray *myMember;
aClass.c
#implementation GameplayScene
#synthesize myMember;
- (id) init {
if ( (self = [super init]) )
{
myMember = [NSArray array];
}
}
In other words, I would like to know if the method setMyMember is being called, or if the pointer of myMember is being modified directly.
Likewise, is myMember = [NSArray array] identical to self.myMember = [NSArray array]?
Without the self. notation, the instance variable is modified directly. With it, the property setter is called (and since you made it a retain property, the new pointer that it's being set to will be sent a retain message).
See Apple's documentation on declaring and accessing properties.

Can I validate a #property value in Objective-C using #synthesized methods?

What it says on the tin: I'd like to use the #property/#synthesize syntax to define a property on my Objective-C 2.0 class, but I want to place restrictions on the range of values allowed in the property. For example:
#interface MyClass : NSObject {
int myValue;
}
#property (nonatomic) int myValue;
Implementation:
#implementation MyClass
#synthesize myValue(test='value >= 0');
Note that the syntax here is just an example. Is this, or something much like it possible? Alternately, what is the literal equivalent of a synthesized setter, so that I can ensure that I use the same object retention rules in my manual setters as is used in a synthesized one.
Assuming your properties are Key-Value compliant (as they would be if you are using #synthesize) you should also implement Key-Value compliant validators. Take a look at Apple's documentation on the matter: http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/Validation.html
The important thing to note is that validation does not happen automatically except when using certain kinds of binding. You either call the validator directly or by calling validateValue:forKey:error:.
You could override the produced setter to call the validator before saving it but if you are using bindings this is probably not what you want to do as the validator will possibly be called more than once for a single modification.
Also note that the validator might change the value being validated.
So lets look at your example (untested, btw. I'm not near a Mac):
#implementation MyClass
#synthesize myValue;
-(BOOL)validateMyValue:(id *)ioValue error:(NSError **)outError
{
if (*ioValue == nil) {
// trap this in setNilValueForKey
// alternative might be to create new NSNumber with value 0 here
return YES;
}
if ( [*ioValue intValue] < 0 ) {
NSString *errorString = #"myValue must be greater than zero";
NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorString
forKey:NSLocalizedDescriptionKey];
NSError *error = [[[NSError alloc] initWithDomain:#"MyValueError"
code:0
userInfo:userInfoDict] autorelease];
*outError = error;
return NO;
} else {
return YES;
}
}
If you wanted to override the synthesised setter and make it do the validation (still untested):
- (void)setMyValue:(int)value {
id newValue = [NSNumber numberWithInt:value];
NSError *errorInfo = nil;
if ( [self validateMyValue:&newValue error:&errorInfo] ) {
myValue = [newValue intValue];
}
}
You can see we had to wrap the integer in an NSNumber instance to do this.
When you use the #synthesize the accessor methods are generated. You can implement your own which will overwrite the generated one.
You can put your own implementation inside the accessor methods, e.g. you can add value checking before assignment and so on.
You can ommit one or the other or both, the ones that you don't implement will be generated because of #synthesize, if you use #dynamic you are specifying that you will provide accessors either at compile or run time.
Accessors will have names derived from the property name myproperty and setMyproperty. The method signatures are standard so it is easy to implement your own. The actual implementation depends on property definition (copy, retain, assign) and if it is read-only or not (read-only doesn't get set accessor). For more details see objective-c reference.
Apple reference:
#synthesize You use the #synthesize
keyword to tell the compiler that it
should synthesize the setter and/or
getter methods for the property if you
do not supply them within the
#implementation block.
#interface MyClass : NSObject
{
NSString *value;
}
#property(copy, readwrite) NSString *value;
#end
#implementation MyClass
#synthesize value;
- (NSString *)value {
return value;
}
- (void)setValue:(NSString *)newValue {
if (newValue != value) {
value = [newValue copy];
}
}
#end