Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
My View Controllers are holding my object properties and iVars etc. They shouldn't hold any at all.
** The rest of this post has been heavily edited for clarity as requested. **
My Class Object using the example given by NSBum that seeks to show data handled by one method, is able to continue being used in another.
#import <Foundation/Foundation.h>
#interface MYEmployee : NSObject
#property (nonatomic, strong) NSString *firstName;
#end
now for my VC interface file
#import <UIKit/UIKit.h>
#import "MYEmployee.h"
#interface MYEmployeeDetailViewController : UIViewController
#property ( nonatomic,strong) MYEmployee *employee; // as suggested
// UI elements
// a button that picks up from a textfield
- (IBAction)getName:(UIButton *)sender;
#property (strong, nonatomic) IBOutlet UITextField *inputField;
// a button that puts the names in a label
- (IBAction)showName:(UIButton *)sender;
#property (strong, nonatomic) IBOutlet UILabel *employeeNameLabel;
// a second label shows how i've been doing it so far
#property (strong, nonatomic) IBOutlet UILabel *contractorNameLabel;
#end
and the relevant essentials in my VC implementation file where i've used a private contractor property of the VC.
#import "MYEmployeeDetailViewController.h"
#interface MYEmployeeDetailViewController ()
#property (nonatomic, strong) NSString *contractor;
#end
#implementation MYEmployeeDetailViewController
// MYEmployee *employee; // use of an iVar has no effect
- (IBAction)getName:(UIButton *)sender {
// MYEmployee *employee = [[MYEmployee alloc] init]; // no effect except only within the method
// employee.firstName = self.inputField.text; //
self.employee.firstName = self.inputField.text; // class object picks up text from a textField
self.contractor = self.inputField.text; // self does exactly the same
}
- (IBAction)showName:(UIButton *)sender {
// MYEmployee *employee = [[MYEmployee alloc] init]; // resets contents of the iVar to nil
// self.employeeNameLabel.text = employee.firstName; //
self.employeeNameLabel.text = self.employee.firstName; // no joy using the property in .h file as suggested
self.contractorNameLabel.text = self.contractor; // this actually displays the text
}
#end
the employee is not to be seen while the contractor shows up for work (typical, we call them 'Blisters' they only show up when the work is done ;)
Back to serious stuff, the [alloc[init]] and iVar lines are commented out as they had no effect, but left in anyway for the sake of completeness. I didn't think I should edit the title, but my quandry is more or less the same in that "..why can't I use the model simply as a substitution of self?" After all, I outsourced the function
I might try to narrow the focus of the question somewhat; but I'll take a shot at what I think the issues are:
My View Controllers are holding my object properties and iVars etc.
They shouldn't hold any at all.
If you're following good MVC principles, then your view controllers should not have properties that would more correctly belong to the model layer. But given that view controllers are the conduit between the model and the view, certain exposed properties on the controller are essential.
Let's say you have a view controller that displays details about an employee - first name, last name, salary, department, etc. You'll probably want a model object that represents the employee. Let's call it MYEmployee. Next think about what other classes need to know about instances of MYEmployee. Those are the properties exposed in the class interface.
Now, how will our controller work with an instance of MYEmployee? Here, the view controller - let's call it MYEmployeeDetailViewController - needs to know what employee to display, so we need to expose the represented employee in the view controller's interface, e.g.
#property (nonatomic, strong) MYEmployee *employee;
Now MYEmployeeDetailViewController class users can pass an employee to display.
then how does one use Class Objects with the expectation i outlined?
I think your uncertainties revolve around questions like "who owns what data?" etc. My recommendation is to take a step back and really read the documentation on Apple's take on MVC and tap into some resources on object-oriented design. It always helps me to think first about interfaces (class interfaces, not UI) and less about implementation. Mapping out the relationships between classes before coding also helps.
You should be calling init on your new myClass object item instead of just alloc for starters. I doubt you really need to only allocate memory but not actually initialize the object.
Beyond that, your question is a bit hard to follow. You use objects to encapsulate data, and then you expose that data to other objects that might need it. Your view controllers will have objects that handle views, and objects that handle data. The controller will get what it needs from the data objects and pass it as needed to view objects. In this way, the controller will hold the data model itself (if you are doing a proper MVC design).
You say you want your class object to hold data between methods: well, that's what instance variables are for.
Related
Let say, I have the code below
self.customObj = self.assembly.customObj() as? NSObject
let temp3 = self.assembly.customObj() as NSObject
If I use TyphoonScopeObjectGraph for customObj, it should return the same instance.
But when I debug, the customObj properties are not the same as shown:
As far as I understand, customObj and temp3 should be the same instance. But as you see in the image, customObj and temp3 have the same ObjectiveC.NSObject address but all of its properties (_shortFormatter, _longFormatter) have different address. What happen? How we can get the same instance for customObj and temp3. An example is very helpful.
Thanks.
You can get the project source code from here
In the example above if you want self.customObj and temp3 to be the same instance, then you need either TyphoonScopeSingleton or TyphoonScopeWeakSingleton.
The way TyphoonScopeObjectGraph works is that during resolution if two instances declare that they depend on another component called context, then the same shared instance of context will be returned. However these are not retained by Typhoon. So you can load a whole object graph (eg a view controller, along with dependencies) and then discard it when done, rather than use singletons, as you might otherwise. TyphoonScopeObjectGraph is also useful for having circular dependencies, such a a controller and view, that has a delegate property pointing back to the controller.
It helps to explain with an example: Let's say we have:
#interface MyViewController : UIViewController
#property(nonatomic, strong, readonly) InjectedClass(ShoppingCart) cart;
#property(nonatomic, strong) InjectedClass(MyView) view;
#end
#interface MyView : UIViewController
#property(nonatomic, strong, readonly) InjectedClass(ShoppingCart) cart;
#end
//ShoppingCart has object-graph scope
#interface ShoppingCart
#end
Now if we ask Typhoon to give us an instance of of MyViewController, then a controller will be returned where both MyViewController and MyView will have the same instance of ShoppingCart
If TyphoonScopePrototype was used, then each would have a different instance of ShoppingCart
If TyphoonScopeSingleton was used, then each would have the same instance of shopping cart, but there'd be no way to release the memory.
. . so you see TyphoonScopeObjectGraph loads an object graph with shared instances, but allows that whole object graph to be discarded after the use-case.
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
Let me first frame my situation. I have a ViewController, which retains an instance of a GameManager. The GameManager class encapsulates the state and logic of a simple game. When certain states are encountered in the GameManager, I would like to perform a set of actions. For instance: when a game is over, I would like to display a game over dialog. As mentioned above, the game logic resides in the GameManager, but the method to create and position a new 'game over' dialog resides in the ViewController. To allow for the GameManager to 'call' back into the ViewController, I passed a reference to the ViewController to the GameManager when allocated, and simply invoke methods on the ViewController. For example:
// GameManager.m
- (void) gameOver {
[self.viewController showGameOver];
}
My question is: Is this the proper, objective-c way to do this? Is there a purer way to handle this? I've though using blocks may be more appropriate.
Implement a delegation pattern here
In your GameManager
#protocol MyGameDelegate : <NSObject>
#required
- (void)gameManager(GameManager *)gameManager gameOverWithSuccess:(BOOL)success;
#end
and declare a property
#property (assign, nonatomic) id <MyGameDelegate> gameDelagate;
in your viewContoller class
// MyGameDelegate implementation
- (void)gameManager(GameManager *)gameManager gameOverWithSuccess:(BOOL)success {
if (success) [self.viewController showGameOver];
}
do something like this but be careful
#property (assign, nonatomic) id <MyGameDelegate> gameDelagate;
there you are not using strong
Finally set gameDelagate as your viewcontroller
hope you have understood.
I notice that Apple has what seems to be duplicate variable names:
2 properties and two ivars. Why does Apple do this?
//.h file
#interface TypeSelectionViewController : UITableViewController {
#private
Recipe *recipe;
NSArray *recipeTypes;
}
#property (nonatomic, retain) Recipe *recipe;
#property (nonatomic, retain, readonly) NSArray *recipeTypes;
And they then update the recipe instance below. Why have two variable with the same name?
Will one affect the recipe variable of the parentViewController since that recipe variable was set when presenting this view controller the code was in from the parentViewController?
//.m file
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// If there was a previous selection, unset the accessory view for its cell.
NSManagedObject *currentType = recipe.type;
if (currentType != nil) {
NSInteger index = [recipeTypes indexOfObject:currentType];
NSIndexPath *selectionIndexPath = [NSIndexPath indexPathForRow:index inSection:0];
UITableViewCell *checkedCell = [tableView cellForRowAtIndexPath:selectionIndexPath];
checkedCell.accessoryType = UITableViewCellAccessoryNone;
}
// Set the checkmark accessory for the selected row.
[[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryCheckmark];
// Update the type of the recipe instance
recipe.type = [recipeTypes objectAtIndex:indexPath.row];
// Deselect the row.
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
UPDATE 1
This code is from Apple's iPhoneCoreDataRecipes core data example:
First have a look at the RecipeViewController's didSelect delegate method, which will present the TypeSelectionViewController (child) view controller. Then have a look at that viewcontroller's didSelect delegate method where you will find the code implementation.
The reason I started looking at this is because I was interested how the parent's tableView cell got updated based on the selection in the ChildViewController in editing mode.
To see this for yourself, do the following:
Run the application
Select the Recipes tab
Click on a recipe - Chocolate Cake.
Click the edit button on the top right
Make note of the current category - should be on desert - then click on it.
Then you will be taken to the child view controller
Click on a different category, then click back and you will notice that the category button for that recipe has magically been updated. And I don't know how that's happening.
Does it have something to do with the private ivars and properties? which affects the parentViewController's cell?
My question i Guess is, how does selecting a category type in the child view controller's table affect the cell.text in the Parent View Controller's table? I can't see where the managedObjectcontext is saved in the child view controller for it to automatically update the parent View controller's cell text.
What you're seeing here is relatively old code, and there's not much need to do this anymore, thanks to Objective-C auto-synthesis.
Nowadays, when you issue a #property (nonatomic) NSArray *foo;, you implicitly get a #synthesize foo = _foo; in your implementation file and an instance variable declaration in your header. You don't see this, the compiler "inserts" it automatically. foo is the property and _foo is the instance variable. (In your above example, the #property and backing instance variable are both the same name, which could get confusing very quickly. With the foo property, you couldn't accidentally say self._foo, that doesn't exist. There's self.foo and _foo. With your example recipe is the ivar and self.recipe is the property. Very easy for one to quickly confuse the two when reading code.
Before the auto-synthesis, there was an intermediate step where you still needed a #synthesize, but you the backing instance variable was generated for you. These new features help you remove boilerplate code.
Answering Update 1
The code doing what you're wondering is in tableView:cellForRowAtIndexPath. There's nothing magical here. When you selected a new Category via the TypeSelectionViewController, the NSManagedObject is updated. Back in the RecipeDetailViewController, cellForRowAtIndexPath pulls the lasted information from CoreData. text = [recipe.type valueForKey:#"name"];
You might be getting confused about what an #property really is. It's just syntactic sugar. A #property these days automatically creates accessor and mutator methods and a backing ivar. Properties themselves aren't areas to store data, it's just a quick way of generating some methods and backing ivars.
Example
#interface MyClass
{
NSUInteger _foo;
}
#end
#implementation MyClass
- (NSUInteger)foo
{
return (_foo)
}
- (void)setFoo:(NSUInteger)newFoo
{
_foo = newFoo;
}
#end
is equivalent to:
#interface MyClass
#property (nonatomic, assign) NSUInteger foo;
#end
You save a lot of typing. When you get into things like NSString properties and different property modifiers like strong or copy, the amount of code you save (and memory management mistakes you avoid) in the mutators becomes much greater.
Your .h file should be your public api. You can re-declare your properties in your .m, implementation file, which are also considered private. For example
.h
#interface MyViewController : UITableViewController
#property (readonly) NSString *name;
#end
.m
#implementation MyViewController
#property (readwrite) NSString *name
#end
Here we are declaring a public name property that is readonly and in your implementation you 're re-declaring the property so that you can use the setter accessor.
I have an application that is to help troubleshooting a device. To do this troubleshooting I make suggestions and then ask for user input with a question that they must answer to continue. Depending on the answer I load a new view with a new set of suggestions and question. I am not sure if I understand the Model part of the MVC setup. I have a model that contains suggestions, suggestion_images, questions, answers, ... I also then link the questions to new model objects. I would just like to know if this is considered best practice? Or am I misunderstanding the MVC design scheme?
EDIT: Here is my model overview:
#import <Foundation/Foundation.h>
#interface troubleshootingInfo : NSObject {
NSString *stepTitle;
NSString *stepCount;
NSString *description;
NSString *imageTitle;
NSString *descriptionImageLink;
NSString *questionTitle;
NSMutableArray *actionsToPerform;
NSMutableArray *actionsStatus;
NSMutableArray *actionsImage;
NSMutableArray *actionImageTitle;
NSMutableArray *logActions;
troubleshootingInfo *nextNoObject;
troubleshootingInfo *nextYesObject;
// This is to be used with selections
NSMutableArray *userInputForAction;
}
//perform function on the set values
- (int) setActionRowHieght:(int)actionID; // get row hieght for table cell
- (int) setDescriptionRowHeight; // get row hieght for table cell
- (int) setQuestionTitleHeight; // get row hieght for table header
- (int) actionCount; // the number of actions in the actionsToPerform array
- (BOOL) isAction; // is action set
- (BOOL) isDescription; // is description set
- (BOOL) isQuestion; // is question set
- (BOOL) isActionPerformed:(int)action; // check if action is performed
- (BOOL) isActionImage:(int)action; // check if action has an image
- (NSString *) updateActionPerformed:(int)action;
Any help would be great.
Thanks
EDIT: fixed the code per suggestions (I think).
My header file now.
#interface TroubleshootingInfo : NSObject
// values of the class
#property NSString *stepTitle;
#property NSString *stepCount;
#property NSString *description;
#property NSString *imageTitle;
#property NSString *descriptionImageLink;
#property NSString *questionTitle;
#property NSMutableArray *actionsToPerform;
#property NSMutableArray *actionsStatus;
#property NSMutableArray *actionsImage;
#property NSMutableArray *actionImageTitle;
#property NSMutableArray *logActions;
#property TroubleshootingInfo *nextNoObject;
#property TroubleshootingInfo *nextYesObject;
// This is to be used with selections
#property NSMutableArray *userInputForAction;
//perform function on the set values
- (int) actionCount; // the number of actions in the actionsToPerform array
- (BOOL) hasAction; // is action set
- (BOOL) hasDescription; // is description set
- (BOOL) hasQuestion; // is question set
- (BOOL) isActionPerformed:(int)action; // check if action is performed
- (BOOL) ihasActionImage:(int)action; // check if action has an image
- (NSString *) updateActionPerformed:(int)action;
Ok sorry for the confusion. My question is related to hierarchical data models and table views. After reading through Apples documentation I feel I have a better idea of how a complex table should be handled. I believe the class I have featured here actually needs to be apart of a much more complex data model. I will rethink my design. Thank you.
That looks much more like a Controller-style object than a Model object. The Model in a quiz domain should have absolutely nothing to do with things like row height! The Model is the domain: questions and answers. Maybe "next question" (it depends on whether the choice of next question is determined by the previous answer, as in an adaptive test).
The classic MVC triad is pretty straightforward:
The View reflects the state of the Model
The Controller structures and presents the View and other Controllers
The Controller also tracks the state of the Model and can modify the UX and...
The Controller makes requests of the Model to modify its state ("Next question")
In iOS, the View will typically be subclasses of UIView, often which have other UIView elements and widgets as members. The Controller will be some form of UIViewController. The Model are in a form appropriate to the domain.
Since you're talking about row heights, I suggest that you read some tutorials about UITableViewController.
I'd make a couple adjustments:
Naming: Classnames should be capitalized.
Properties: The ivars listed between braces can be replaced by properties. Compilation will generate getters and setters automatically.
Design: The methods that mention UI layout attributes, like rowHeight, do not belong to the model. The view controller usually concerns itself with this sort of thing (for example, as the datasource for a table view). Most likely, the code you have in those methods belongs in your view controller.
A few observations
Use a capital letter for class names (TroubleshootingInfo).
Don't use ivars directly, use properties. Properties that should be private (i.e. not used by other classes directly) can go in a () category (#interface TroubleshootingInfo () in the .m file.
The model should not know anything about row heights. That is for the view (and occasionally view controller).
Your isAction, etc. bools sound like they would better be hasAction or isActionSet, etc. They are asking if the object has a property, not if the object is that property.
I have a storyboard in which I have specified a parent view controller and two container views (made up of two UITableViewControllers). In my parent view controller I have buttons that are used to filter the content of the two tables.
My problem is figuring out how to send messages to the container views to perform these filters. I imagine we use delegates but is there a best practice way of implementing these delegates?
Subject to some caveats, you could define properties for each of the two contained tables, connect the outlets in your .xib, and message them directly in your button handlers.
For example:
#interface ParentViewController : UIViewController
#property (nonatomic) IBOutlet Table1Class *table1;
#property (nonatomic) IBOutlet Table2Class *table2;
#end
#implementation ParentViewController
...
- (IBAction)table1FilterButton:(UIButton *)sender
{
[self.table1 filterBy:...];
}
- (IBAction)table2FilterButton:(UIButton *)sender
{
[self.table2 filterBySomethingElse:...];
}
#end
Now, the caveats - you probably won't want to do this if you anticipate that the number of contained view controllers is likely to grow significantly, as it will be unwieldy to have table1, table2, table3, ..., tableN. You'll probably also want to find a way to extract a common interface (in the form of a protocol) from the two contained view controllers, in order to write less divergent code for handling the filtering of each table.
Maybe something like this, instead:
#protocol ContainedTableProtocol
#property (nonatomic) NSPredicate *contentFilterPredicate;
#property (nonatomic) NSComparator sortComparator;
#end
#interface ParentViewController : UIViewController
#property (nonatomic) IBOutlet UITableViewController<ContainedTableProtocol> *table1;
#property (nonatomic) IBOutlet UITableViewController<ContainedTableProtocol> *table2;
#end
#implementation ParentViewController
- (IBAction)filterTable1ButtonAction:(UIButton *)sender
{
[self filterTable:self.table1];
}
- (IBAction)filterTable2ButtonAction:(UIButton *)sender
{
[self filterTable:self.table2];
}
- (void)filterTable:(UITableViewController<ContainedTableProtocol> *)table
{
// Create predicate and comparator as needed...
NSPredicate *predicate = ... ;
NSComparator comparator = ... ;
table.contentFilterPredicate = predicate;
table.sortComparator = comparator;
}
#end
This uses a common interface to apply the filtering operations to each table view controller, and then codes to that interface rather than an API specific to a particular Table1Class or Table2Class.
You can see the answer at How do I create delegates in Objective-C?.
The simpler way is declare the delegate in the Childs and implement in the parent (ie: The childs send data to the parent).