Using an allocated and initialised object outside of viewDidLoad? - objective-c

I hope the question title is adequate
Just confused about something in a piece of code I have seen in an online tutorial. There is a generic ZHEDog class with declared properties, methods, etc. and from this class we have created several instances - firstDog, secondDog, fourthDog, and so on.
Now, when we created each instance we did so inside of the viewDidLoad method of our main(one)view controller with the line:
ZHEDog *fourthDog = [[ZHEDog alloc] init];
and then we set some of its properties like name, and so on, here after this line.
So we did this instance creation in the view controller's viewDidLoad and so far have not subclassed the generic ZHEDog class, so it is all deriving from the one class file.
Now, where I am confused is that apparently I cannot set a property of this instance in another method (other than viewDidLoad), so I can't say something like:
-(void) printHelloWorld
{
fourthDog.name = "something new";
}
It kind of makes sense but I can't explain why. I would have thought once the instance was allocated and initialised I could change its properties where I wanted to if necessary? But do the same rules of scope apply to viewDidLoad?

Use properties, they are like instance variables accessible from everywhere within the instance of the class
#property ZHEDog *firstDog, *fourthDog;
then instantiate them in viewDidLoad
self.firstDog = [[ZHEDog alloc] init];
self.fourthDog = [[ZHEDog alloc] init];
and change them in a method
-(void) printHelloWorld
{
self.firstDog.name = "woof";
self.fourthDog.name = "something new";
}

What #vadian has is correct, but using properties also allows other classes to see this variable. Say if you imported the header file and it contained #property ZHEDog *firstDog, *fourthDog;. These variables become public. unless they're in the implantation file.
But other approach is creating variables like so:
Header File
#interface ViewController : UIViewController {
ZHEDog *firstDog, *fourthDog;
}
#end
All will be the same expect now the values are private, or exclusive, to just the ViewController. therefore not allowing others to use or see these variables. And to access the variables in your function printHelloWorld:
- (void)printHelloWorld {
firstDog.name = #"woof";
fourthDog.name = #"something new";
}
Allocating
- (void)viewDidLoad {
//new is the combination of alloc and init. only use +new when using alloc and init, not alloc and initWith...
firstDog = [ZHEDog new];
fourthDog = [ZHEDog new];
}
i hope this would better your goal :)

Related

obj c: accessor: self vs myInstance

In Apple's The Objective-C Programming Language p. 18, they make a distinction between setting a variable with self versus instance reference. e.g
myInstance.value =10;
self.value =10;
1. Would these two set different properties named value?
2. How could self work if there are several instances with properties named value?
They also assert, "If you do not use self., you access the instance variable directly." This would mean that the accessor would not be called if you use
myInstance.value =10;
and KVO wouldn't work. Is this true?
3. Using #Property and #Synthesize (with garbage collection), what is the proper way to set properties of different instances? And what good is the self reference?
A numeric example would help me, please.
1 - Would these two set different properties named value?
No, I think you misunderstand what the guide is saying when it makes a distinction between self.value and myInstance.value. In both cases the setter function (i.e., setValue:) is called.
You use self to access your own properties (that is, referencing properties from within functions in a class that you wrote). Like:
#interface MyObject : NSObject
#property( nonatomic ) NSInteger value;
- (void) doSomething;
#end
#implementation MyObject
#synthesize value;
- (void) doSomething
{
self.value = 10;
}
#end
Whereas you'd use myInstance to set a property in some other variable, from outside that class.
MyObject* anObject = [[MyObject alloc] init];
anObject.value = 10;
2 - How could self work if there are several instances with properties named value?
It wouldn't. See above.
They also assert, "If you do not use self., you access the instance variable directly." This would mean that the accessor would not be called if you use myInstance.value =10; and KVO wouldn't work. Is this true?
No. self.value and myInstance.value both call their accessors (setValue: in this case), and KVO will work. What that assertion means is that if you access an ivar from within your own class, not using the accessor, KVO will not work.
#interface MyObject : NSObject
#property( nonatomic ) NSInteger value;
- (void) doSomething;
#end
#implementation MyObject
#synthesize value;
- (void) doSomething
{
self.value = 10; // This invokes the accessor, and KVO works.
value = 10; // This just sets the instance variable, and KVO won't work.
}
#end
Using #Property and #Synthesize (with garbage collection), what is the proper way to set properties of different instances? And what good is the self reference? A numeric example would help me, please.
Just as shown above, use the instance name. self is only used for accessing properties within a class. Examples above.
The best way to under stand self is to think of how it is implemented, as a hidden argument with every method call so the method -[UIView drawRect:] has a c function implementation like
BOOL drawRect:( UIView * self, SEL _cmd, NSRect r ) { }; // of cause : is not legal in c
and calling the method is a little like (ignoring the dynamic look up)
UIView * v = ...
NSRect r = ...
drawRect:( v, #selector(drawRect:), r );
so if you invoke a property in the drawRect: implementation you are doing it for the hidden object parameter called self.
Accessing the instance variable directly will stop KVO from working, but sometimes you want that, for example when initialising them perhaps.
IF you mean automatic reference counting when you say Garbage Collection, most of the time for objects you want them to be strong or copy, immutable strings using copy will be turned into a retain and if it is mutable then you often want a copy to protect against the original being changed underneath you.
One potential issue with strong is that you can end up with circular references where if you follow the links around you comeback to the original object so each object is indirectly retaining itself and you have a catch-22 situation where the object has to release itself before it can release itself. So in these situations you need to use weak. You can usually workout who should retain and who should weak by think about which object conceptually owns the other.
For non-object you have to use assign.
self.property and [self method]; are strictly used within a class to refer to itself. You do not ever refer to the object within itself with anything but self.
On the contrary, use instances of an object to refer to an object from another class. For instance, I would refer to a UIImageView from my viewController in a way like:
UIImageView* imgView = [[UIImageView alloc] init];
[imgView setFrame:CGRectMake(0,0,320,480)];
But if I were editing a subclass of UIImageView that I called, say rotatingImageView:
#implementation rotatingImageView
-(id)init
{
//Super instantiation code that I don't remember at the moment goes here
[self setFrame:CGRectMake(0,0,320,480)];
}
This is just an example of a method.
Once again, you use self strictly within its own class, and you use other variables to reference an instance of another class.
Hope that makes sense.
My big problem was how an ivar and a property could be tied together when they have different names, especially with multiple ivars.
I finally found that if name of property doesn't match name of ivar, a new ivar is synthesized. This is accessed by self.propertyname (within object) or object.propertyname (outside of object), not the declared ivar.
To tie disparate names of ivar and property, equate them as in
#synthesize propertyname = ivarname.
Thanks to
http://blog.ablepear.com/2010/05/objective-c-tuesdays-synthesizing.html

Load custom class properly

I have a custom class which I want to "load" inside the firstViewController and then access it from other classes by segues. My Problem is, I can't even access and change the instance variable inside the firstViewController. Somehow I'm "loading" it wrong. Here is the code I used until now:
inside viewController.h
#property (strong, nonatomic) myClass *newClass;
inside viewController.m
#synthesize newClass;
I then try to access it by:
self.newClass.string = #"myString";
if(newClass.string == #"myString"){
NSLog(#"didn't work");
}
Well, I get "didn't work". Why is that?
When I write
myClass *newClass = [myClass new];
It does work. But the class and its properties gets overwritten every time the ViewController loads again.
What would you recommend? Thank you very much.
Like Kaan said, you forgot to initialize your class, You have only declared and created a pointer for it but not the actual object, on your ViewDidLoad add
self.newClass = [[myClass alloc] init];
It does work. But the class and its properties gets overwritten every
time the ViewController loads again.
That's because every time that specific Viewcontroller loads you are reinitializing the class.
If you want a persistent class through all your program look for the singleton pattern.
This is used in the case when you want to have only 1 instance of a certain object, if you try to initialize another instance of that object you will just receive the one you already have.
PD: newClass.string == #"myString" is wrong.
Use the isEqualToString method when comparing strings.

Cocoa textfield returning null

Sorry if it's a bit long but I want to be as descriptive as possible.
I'm making a test application with several classes and 2 nib files (each class is the owner of one nib). One of the nib files has a textfield, the other has a button. I'm trying to log whatever is inside the text field when the button in the other nib view is pressed, but it returns (null). In one of the classes (ViewClass) I have this:
- (IBAction)startAction:(id)sender {
MyClass *anInstance = [[MyClass alloc] init];
NSString *string= [anInstance name];
NSLog(#"startAction logged: %#", string);
"Name" is a property of MyClass. What I want to do is have "name" set in the init of MyClass, that way, when anInstance is initialized, the MyClass init method does this:
- (id)init {
if ( self = [super init] ) {
[self setName:[nameInput stringValue]];
NSLog(#"init value: %#", name);
}
return self;
"NameInput" is the textfield. I thought this would return whatever was in the textfield, but I get null instead. When I use setName:#"text" it gets passed fine, so something is wrong with the text field.
I previously did this with my own getter, and in that case, it didn't return null when the method was called from it's own class, but if it was initialized and called from the other class, then it returned null, I used this:
- (NSString *)name {
NSLog(#"nameMethod = %#", [nameInput stringValue]);
return [[[nameInput stringValue] retain] autorelease];
This way, I can tell it is all properly set up, but something is happening when I init MyClass from the ViewClass, and try to get "name", that it keeps saying that the text field is null.
Not sure if it helps but the nib with the button belongs to MyView (which is a subclass of NSViewController) and the textfield belongs to MyClass (subclass of NSObject).
Someone suggested the field was not properly linked, but if that was true, it wouldn't have worked when called from it's own class, but it did. Someone else mentioned it might be a problem with the textfield being initialized to nil, so I tried the init thing above. Neither has worked so far.
Thanks for the help.
IBOutlets are not guaranteed to be hooked up until awakeFromNib, which is after your objects' init methods have run. You'll need to do your nib setup in awakeFromNib.
EDIT NOW THAT I'VE GOTTEN HOME: Sorry, I didn't read carefully enough before. What I said above was true, but there's a deeper problem as well. I see now that you also are dealing with two different objects — one in the nib, one created in code. If you have an object in a nib with an outlet hooked up to an interface element, that doesn't make other objects of that class also have an instance variable referring to the element. Two independently created MyClass instances don't share the same instance variables any more than every NSArray in your program holds the same set of items. If you want to use the instance from the nib, you'll need to use that instance.
How you do this is a matter of how you structure your program. There's no simple [self magicallyGetObjectFromNib]. Somehow, one object needs to either find the other (say, by knowing the nib's owner) or be told about the other by an object that knows about them both.
You're in the init routine when you try to extract a value from a field in the same object, and apparently expect it to be initialized. 'Tain't gonna happen.

property and synthesize : in this code, without "self"?

i have a simple question :
if we don't use here the dataController property in another class in the project, we don't really need to use the "#property" and synthesize and we could have just done a simple "=" operation with dataController = controller, like in the second chunk of code? :
DataController *controller = [[DataController alloc] init];
self.dataController = controller;
[controller release];
rootViewController.dataController = dataController;
Second one :
DataController *controller = [[DataController alloc] init];
dataController = controller;
So if we don't need a property outside the class, we could just do it this way?
Thanks
You are correct, if dataController is a retaining property.
If you are just using it once, there is not much to gain with a property. If you change its value often (i.e. assign a different DataController) then even a private property can make sense just to make the memory management easier.
If dataController is not gonna be used in any other class then you don't need to synthesize it. Then Yes, you could just create it inside that class.
(Of course, if your dataController in your rootViewController also need a reference to it, then you need to handle that as well)
Yes, the second example works fine, provided dataController is declared as an iVar and not just a local variable to the method where it is used - but you probably are aware of that.
In both examples you of course need to release the stored value in the dealloc method.

How to keep Cocoa bindings working after replacing the target object's instance with another instance of the same type?

I would like to be able to use bindings to keep my GUI synchronized to a dynamically loaded object, but as soon as I replace the object in question with another one of the same type the bindings break and the GUI stops updating. Here's some code to help you understand what I mean:
In my interface I have an instance variable to hold the object in question:
#interface AppDelegate : NSObject {
CustomObject *anObject; // This object has a "NSString *textValue" property
}
Then in my implementation I instantiate the object:
- (id) init {
self = [super init];
if (self != nil) {
anObject = [[CustomObject alloc] init];
}
return self;
}
In Interface Builder I have the "value" of a text field bound to "anObject.textValue".
When I call this method:
[anObject setValue:#"Changed anObject.textValue!" forKey:#"textValue"];
then the text field updates to reflect the new value.
But what I want to do is display the values from an object which is given after doing some work elsewhere in the application. So what I did was this:
- (void)setCustomObject:(CustomObject *)newObject {
anObject = newObject;
}
Now the result of this operation seems to break the bindings from the GUI to the CustomObject instance (anObject) which seems logical considering the bound object has been replaced by another instance.
What I want to know is if there is a way to keep the bindings functional with the dynamically created instance of CustomObject without having to re-bind every control programmatically through bind:toObject:forKeyPath:options: or similar which would require (to my knowledge) the use of IBOutlets to get a hold of the controls to then be able to bind them to the values in my new object (IMO this would make the bindings kind of useless in my situation). Is this the only solution or is there a better, cleaner way to deal with this?
I have read a good bunch of documents on developper.apple.com and elsewhere regarding bindings but I did not find anything which seems to talk about this particular case.
Thanks in advance for your time!
To be specific, I think the problem was that your setter method was called -setCustomObject: instead of -setAnObject:. If you made just that change I think that KVO would be invoked, and your bound textfields would be updated.
Abizern's note about it leaking (if you're not using GC) still applies though. Your setter should instead look something like:
- (void)setAnObject:(CustomObject *)newObject {
if (anObject != newObject) {
[anObject release];
anObject = [newObject retain];
}
}
Have a look at these docs on Key Value observing. This should show you how to change properties in a KVO compliant way.
Alternatively, set up anObject as a property:
#interface AppDelegate : NSObject {
CustomObject *anObject; // This object has a "NSString *textValue" property
}
#property (retain) CustomObject *anObject;
...
#end
#interface AppDelegate
#synthesize anObject;
...
#end
Then when changing the anObject instance, use property syntax.
self.anObject = newObject;
This will take care of the KVO stuff for you.
note:
Unless you have GC turned on your setCustomObject: method leaks.