I have a ViewController that initializes another class that loads data into a mutable array and saves it as a property on itself.
here is my ViewController init code:
-(id) initWithCollectionID:(NSString *)aCollectionID {
if (self = [super init]){
collectionID=aCollectionID;
dataSource = [[CollectionListDataSource alloc] initWithCollectionID:collectionID];
}
return self;
}
once dataSource has loaded all of the data into it's property dataSource.collectionItems I set dataSource.loaded = #"true";
how do I use addObserver to watch that value and fire off a function in my ViewController?
something like this I'd assume:
[self addObserver:dataSource forKeyPath:#"loaded" options:NSKeyValueChangeNewKey context:nil];
Then what do I do?
As your code stands now, it will pause until the data is loaded regardless of whether you use notifications or not. It will not progress past:
dataSource = [[CollectionListDataSource alloc] initWithCollectionID:collectionID];
...until the CollectionListDataSource object has completed its own initialization (which I presume also means the loading its data) and returns an instance of itself.
If you want the CollectionListDataSource object to load while the view controller keeps on initializing, you will need to put the CollectionListDataSource object on another thread. However, you can't have an attribute object running on separate thread.
You seldom need to jump through such hoops. Unless this array is very large (10k+ objects) you most likely don't have to worry about. In most cases, you need the data before the view can function anyway so there's no point in letting the view go on without the data.
If you do need actually need to observe an attribute of another object, see Key-Value Programming Guide: Registering For Key-Value Observing for details.
Related
I'm trying to pass int variable to UITableView through UINavigationController (I'm using xcode 4.3) So I created 2 classes (PartsTableViewController that is "UITableViewController" and PartsNavController that is "UINavigationController"), I want to pass the variable from my current class to PartsTableViewController and then open that table with its Navigation controller that contains the title bar , so I wrote in my current class the following code:
PartsNavController *partsNav = [self.storyboard instantiateViewControllerWithIdentifier:#"partsNav"];
partsNav.groupId = myGroupp.bg_id;
[self presentModalViewController:partsNav animated:YES];
and in the PartsNavController class I wrote in viewDidLoad:
PartsTableViewController *parts = [self.storyboard instantiateViewControllerWithIdentifier:#"Parts"];
parts.groupId = groupId;
[parts.tableView reloadData];
and in PartsTableViewController I wrote in viewDidLoad:
NSLog(#"This is group: %d", groupId);
but when run, it generates the output 2 times,
This is group:1
This is group:0
first time is the value that I sent and the second time it outs 0 , I just want the value that I sent, not 0
how can I prevent this and get just the value that I sent ????
I want to pass from (MaktabatyTableViewController) to (PartsTableViewController) without using segue
The better way to do what you want is to push second TableViewController in existing UINavigationController. The easiest way to do that is to create that NavContr in StoryBoard and than to TableViews and connect it's cell with leading view controller with segue. And than use method below:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UIViewController *destViewController = segue.destinationViewController;
destViewController.integerValue = value;
}
I want to pass from (MaktabatyTableViewController) to
(PartsTableViewController) without using segue
There are (at least) two strategies you can choose from:
Direct communication: People often ask how to communicate between two objects, and it almost always boils down one of the objects having a reference to the other (and sometimes vice versa). To send a message to an object, you need a pointer to that object; if you've got the pointer, there's no mystery about how to communicate. Thinking about it in those terms helps you think about the issue a little differently: instead of the immediate "how do I send a message to that object?" you can instead focus on the relationship between the two objects. How was each one created? Is one of the objects the parent of the other? Is there some common parent object that can provide a pointer? How should the objects be related, if at all?
Indirect communication: Sometimes instead of having two objects communicate directly, it's more appropriate to route the communication through some intermediate object. For example, your MaktabatyTableViewController might send a message to its delegate, and the delegate could then pass the information on to PartsTableViewController. A much more general solution is to use notifications: MaktabatyTableViewController could post a notification that PartsTableViewController listens for. The intermediate object in this case is the notification center. Either way, the advantage that you get with indirect communication is that neither object has to know about the other. That reduces coupling between the two classes and makes them both more flexible and more reusable.
From what I can see in your question, I'd suggest using notifications.
I ask this question because it appears that viewDidLoad gets called before the main block of the initializer does and the class variables I'm initializing in the initializer are turning up nil in viewDidLoad. For your reference, I'm doing this entire viewcontroller programmatically and I've created a custom initializer so I can take in various parameters to be used in my viewcontroller. My custom initializer calls UIViewControllers designated initializer of course.
Basically, I'm curious about what is the proper design pattern for sending in parameters to a UIViewController? I've read other threads about this and haven't really gotten a definitive answer. Should I just skip the initializer and set the properties manually (from outside the class)? That seems kind of bleh, I'd really like to send in some parameters and am curious how others go about doing this?
The reason viewDidLoad is called before initialization completes, is probably because you call the view method in the initializer. For example:
- (id)init
{
if ((self = [super init])) {
_thing = 123;
_other = self.view.frame.size.width / 2;
}
return self;
}
viewDidLoad is called when the view loads. And the view loads as soon as you call the view method (or access the view property, if you prefer). So you should avoid referencing the view in init.
To answer your question, I prefer to create an init method for my view controllers.
- (id)initWithThing:(MyThing *)thing thang:(MyThang *)thang
{
if ((self = [super init])) {
_thing = [thing retain];
_thang = [thang retain];
// ...
}
return self;
}
You can also use properties to set extra variables after initing. Personally I prefer to do this with optional properties only, though, and put required properties in the init method. That way I can't init the view controller with an invalid state.
I encapsulate the state of my controllers in an additional state object. So the controller usually has ivars for the GUI elements and a reference to this state object.
State objects are handled by a StateManager object on my delegate. So instead having a controller referencing another controller and set variables directly, all changes go through this manager. A little more work but way less messy.
Any class is able to retrieve the state for any other controller and change it. This is the purpose of some controllers (eg: choosing a video from youtube happens on a dedicated controller). But usually is just one class getting a state to push the next controller with initWithState:. The pushed controller then applies the state to its GUI in viewDidLoad or changes this state object passed from the previous class.
The StateManager keeps a dictionary of all the state objects. I keep the state object graph light, eg: I store a reference to a image, but not the image itself. The real beef of the application is in Core Data, I only use all this to handle the GUI state. The StateManager listens for the application resign event and saves the state dictionary to disk using NSCoding. The state object ivars are always nil or some meaningful value, no dangling pointers.
I'm working on my 2nd iPhone app and this seems to be working, but I wonder too how other people does it. Any input is welcome.
Basically you should have properties defined for your input data but add a custom init-function.
If you create a custom init-Method you should be fine - remember that initWithNibName:bundle: is the main initializer of UIViewController so this is what you want to call from your custom init-Method. viewDidLoad will always be called after your init-Method at the first usage of customVC.view (either from your code or via Framework):
- (id)initWithDataObject:(MyDataObject*)obj
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.dataObj = obj;
}
return self;
}
I will try and explain this as best as possible if I have this code here
ViewTwoController *home = [[ViewTwoController alloc] initWithNibName:#"contentscreen" bundle:nil];
[self presentModalViewController:home animated:YES];
[home release];
I will start a new .m and .h class. What I would like to try and do however is when this is called, have the .m and .h class where it was called from running in the background so I do not lose data.
The best example I can think of is with Android. If you begin a new class, and don't add the finish() statement in the class the call was made from, the previous class runs behind the current class (that was pushed to the front) and maintains all the data it originally had, so if you hit a return button, you will see the information you had moments ago. Is this possible? I can try add more detail if people cannot understand what I am trying to do.
You need to understand a objects life cycle a little better.
An object is brought into existence generally with a 2 part process.
allocation - (grabbing the memory for the object and its members)
initialization - (setting the object up for use)
This can be combined into single step with the +new class method which combines alloc and init.
lets introduce an example class called MyClass and an object of that class called myObject. By convention classes start with uppercase letters and objects start with lowercase letters. So Without further ado, some code:
MyClass * myObject;
this this makes an object pointer, but doesn't allocate any memory for it or direct the pointer to reference anything.
myObject = [[MyClass alloc] init];
this actually creates an instance of MyClass, passes the -init message to it, then assigns the return value of the init message to myObject. At this point the reference count of this object is 1.
myObject can potentially go out of scope but that alone doesn't free the memory that was allocated during the alloc step.
in order to free that memory a release message will need to be passed to the object.
[myObject release];
The effect of release is to decrement the reference count, if the reference count is already 1 then the object will then be passed the -dealloc indicating that it is actually being freed.
back to your question... essentially [self presentModalViewController:home animated:YES]; ends up calling -retain on home, so that it will not be destroyed until you dismiss the modal view controller. In affect when you call release or autorelease you aren't dealloc'ing the object, just telling the object:
"Hey, I don't need you anymore, and if no one else does either then free up all the memory that you grabbed earlier".
Your problem has nothing to do with "class running in the background" but more with how you manage your data.
When you present a modal view controller, its parent (the view controller you presented it from) isn't destroyed (unless you specifically release it, which would probably crash your app later). So if you're wondering whether its still in memory; it is. As for tasks still running, it depends on what those tasks are. For example, you can still send it messages (call methods) and it will gladly receive those messages from your or from a delegate and perform whatever action it has to while it's off-screen.
Hope that helped.
In this case you are presenting new view controller. The main thread will be in the new controller presented. If you want something to run in background in the previous view controller then you can create a background thread. This can be done using [self perfomselectorInThebackground ... ] Or some other methods like GCD. (The main thing is you should not block Main thread)
Please be nice.. I am a noob with this stuff. I want to allocate and use an object through a method that I call many times, and instead of making it a global variable and allocating it in my initWithFrame method I want to alloc it in just the one method, and make it a local variable that I allocate then and use then.
You need to step back and think about how many different things need to access said object.
If it is everyone, then you should likely implement the shared instance pattern. Something like:
+ (MyObject *) mySharedObjectThatEveryoneUses
{
static MyObject *sharedInstance;
if (!sharedInstance) {
sharedInstance = [MyObject alloc];
sharedInstance = [sharedInstance init];
}
return sharedInstance;
}
Note that the above does not account for threading on initialization. I.e. if multiple threads simultaneously call the method for the first time, you might end up with multiple instances. Also note that the splitting of +alloc and -init is a pattern that is unique to this situation. Doing so avoids the situation where the -init method causes + mySharedObjectThatEveryoneUses to be invoked, creating a second instance before the assignment can happen.
Now, if you are talking about access within an object graph; access to the single instance within some subset of objects in your application, then you will need to have an instance variable somewhere that all of the objects can get to.
For example, say you are implementing a game where there is a Player object and that Player object has a bunch of Piece instances. When a Piece is created, you would typically have a property that refers to the owning player and that would be your single point of reference.
If you need something more transient, you should likely look to what should be your controller layer. I.e. there has to be some object somewhere that can act as a coordinator for the set of objects that need access to that single instance.
If I understood your question correctly, you're looking for a static variable. A static variable keeps its value like a global variable, but it "lives inside" a function like a local variable. Your function could look something like this:
+ (MyObject *)getSingletonObject {
static MyObject *obj = [[MyObject alloc] init];
return obj;
}
obj would be created the first time you called this method, but calling the method again would return the same object it had already created.
What you want is called a Singleton. You can read up on Apple's recommended way to create singletons here:
http://developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32
Look for "Creating a Singleton Instance" in the "Cocoa Objects" section. There are a few more methods it's good to define beyond just a class level initializer.
I'm not entirely sure I understand your question, but you can't really allocate the same object many times.
Each time you want a new instance, you ask the Class object to allocate a new instance for you (yes, classes being objects themselves is mind blowing). So this code:
NSString* s;
s = [[NSString alloc] init];
s = [[NSString alloc] init];
Here you are creating two instances of NSString. This code is bad because we have alloc'd the first instance and have not released, and assigned its only reference (s) to a new instance. This would cause a memory leak.
I have an XML reader class which I initialize with a URL
- (id)initWithURL:(NSURL *)url
This class adds objects to an array in the calling class using an instance variable
// in the interface
ViewController *viewController;
// in the implementation
[viewController addObject:theObject];
Now, I initialize my XML reader class, then set the View Controller separately:
XMLController *xmlController = [[XMLController alloc]
initWithURL:url];
xmlController.viewController = self;
My question is whether I should create a new init function which sets the viewController at the same time.
Thanks.
Edit: I forgot to add that my XML reader starts downloading and parsing the class in the init function.
It's entirely up to you. You can see examples of both styles all over Apple's code.
As long as you don't make any assumption about the viewController property's value being constant over time, it should be fine to leave it as-is.
BTW, you might think about refactoring the addObject: logic into a protocol instead of requiring a specific subclass. Something like:
-xmlController:didDecodeObject:
Or whatever makes sense for your XMLController object's logic.
If your init routine is going to cause delegate/controller calls, or set off asyncronous activities (potentially including your downloading) that could message the delegate, then you should include it in the init function.
Otherwise your controller might miss potential delegate messages such as xmlController:didStartConnection that might be called before your initWithURL routine returns.
Also, if the controller/delegate is a required part of the XMLController activities, then you should include it in your init routine.
So yes, in this case I would suggest:
XMLController *xmlController = [[XMLController alloc] initWithURL:url andController:self];