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.
Related
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 :)
I have the following method-header in a singleton:
-(int) reconnectToServerForClass:(id)myClass
The parameter myClass is always the self-object of the calling class. Different ViewControllers in my project call this method so my question is this:
Is it possible to access myClass.view or what can I do to achieve that?
my intention is that I want to show a progress-hud for every ViewController that calls the method:
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:myClass.view animated:YES];
Isn't there the need for casting myClass before I can access the view-property?
If I understand correctly you can do the following:
-(int) reconnectToServerForClass:(UIViewController *)myClass
{
//myClass.view
}
or inside the method
-(int) reconnectToServerForClass:(id)myClass
{
if([myClass isKindOfClass:[UIViewController class]])
{
UIViewController *viewController = myClass; //edit as suggested by Peter Segerblom
//viewController.view
}
}
Which ever option you choose is that it will depend on how you use it and how you want to manage or limit the callers, in option 1 the parameter must be a sub class of UIViewController and the same for option 2 but the difference is that you can pass in anything but if the type is not UIViewController it will just do nothing.
Disclaimer: This code is not tested and typed from memory, so it might not compile, let me know if you come across issues and I will edit answer.
Yes.
If you want to access it from the singleton class, you make a member or property of which is a pointer to a view, then set that in reconnectToServerForClass: and access it as you would any other member.
If you want to access it from outside, you do the same, and do something like [[MySingleton instance] view]
Be aware that dragons lie ahead of your path, you will need to make sure that views "unset" this property before they are destroyed, otherwise your singleton might try to access a no-longer-existing view instance.
Quick question... Well 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. However, what about if I'm not sending messages to this property or if I set the property using self.property = something? Do I need to alloc init in these cases as well? Also, do UI properties start out as nil as well, such as a UILabel property that you drag out from your storyboard? Do these need alloc init?
Thanks to all who answer
Stunner did a good job of explaining not needing to alloc init objects that have already been created.
But if it is an object that doesn't exist, where are you going to create it? A very common pattern, which I mention because you mentioned it in your post, is lazy instantiation.
So you want an NSMutableArray property. You could alloc init it in some method before you use it, but then you have to worry about "does that method get called before I need my array?" or "am I going to call it again accidentally and re-initialize it."
So a failsafe place to do it is in the property's getter. It gets called every time you access the property.
.h
#property (nonatomic, strong) NSMutableArray* myArray;
.m
#synthesize myArray = _myArray;
- (NSMutableArray*)myArray
{
if (!_myArray) {
_myArray = [[NSMutableArray alloc] initWithCapacity:2];
}
return _myArray;
}
Every time you access that property, it says, "Does myArray exist? If not, create it. If it does, just return what I have."
Plus an added benefit with this design pattern is you aren't creating resources until you need them, versus creating them all at once, say, when your view controller loads or your app launches, which, depending on the requirements, could take a couple seconds.
The reality is when you do self.myProperty = [[Class alloc] init], you're not initializing your property. Rather, you're initializing an object that you tell your property (which is in fact a pointer) to point to. So if you already have an object that's allocated and initialized, you don't have to alloc/init again and you can do self.myProperty = object;
UI Properties do no start as nil, this is because when you add elements in the interface builder, the view owns the elements that you add and these objects are initialized automatically for you. This means if you're creating IBOutlets and connecting them to some properties, you don't have to alloc/init.
I hope this was helpful.
I don't have experience with Storyboards but I know that when you create objects via a xib file all objects are properly instantiated when you tell a view controller to use a xib file. So you need not worry about alloc/initing those objects in code.
Regarding using self.property = <something>, it depends on what something is. If something is any sort of existing object you need not do the alloc init on that object as the self.property = ... syntax calls the property's setter method which will retain, copy, assign, etc. the new value to the property appropriately.
Now any sort of existing object can be an alloc/init'ed object, or an autoreleased object obtained from a convenience method (NSString's stringWithFormat: for example).
As Kaan Dedeoglu pointed out, the self.property = ... syntax points (and retains) the ivar to the object in memory, and it is up to you to initialize that object if it isn't already instantiated.
No you do not need to [[Class alloc]init the properties in your init method.
However, I would encourage you to explicitly set them to Nil in your init method for clarity.
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.
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.