I have created a class called Item that has two pointers to Item, *containedItem and *container. I declare them as follows:
#property (nonatomic) Item *containedItem
#property (nonatomic) Item *container
I override dealloc within Item as follows:
- (void)dealloc
{
NSLog(#"Destroyed: %#",self);
}
so that I may see which items are being destroyed. I create two items and make it so that one points to the other as its container and the other points to the first as its contained. As the default attribute is being a strong pointer, I would think this would lead to a memory leak. However, when I run my program, it shows that both Items are destroyed. I am wondering how the Items can be destroyed when both have a strong pointer to them (from the other).
i want to ask this question to clarify my knowledge.
I have an XML file, which have elements like - name, description, latitude, longitude etc. I decide to make custom class PlaceHolder, with properties like : #property (nonatomic, strong) NSString *name; #property (nonatomic, strong) NSString *description; and other.
What i want is, to add object of that class to other. How should i init this right? And may i add it like a property to other class?
At now, in any function i need to use that class i do following:
PlaceHolder *place = [[PlaceHolder alloc]init];
place.name = #"";
place.description = #"";
Its not very convenient to init this every time, is there any way to add PlaceHolder class like property to other class?
It's not really clear what you are asking. yes, you can add a PlaceHolder property to a class. Then, you can add code that creates a PlaceHolder object where appropriate.
If the class you're creating always needs a PlaceHolder object then you can write a custom init method that creates one and installs it in the property.
If it's a view controller and only needs a PlaceHolder when the view is displayed, you could add code to your viewDidLoad that creates a PlaceHolder and installs it in the property.
A third approach would be to write a custom getter for your property and "lazy load" the PlaceHolder object the first time you reference it.
You need to provide more information if you want better guidance than that.
All the examples I've come across so far ( including Stanford podcasts ) reference a Model by declaring it as a property of a View Controller and using it from there:
#import "myClass.h" // assume it carries a single NSString property
#interface
#property (nonatomic,strong) myClass *myobject;
#end
#implementation ViewController
-(void)viewDidLoad {
self.myObject = [[myClass alloc] init]
.
.
.
-(void)someMethod{
displayLabel = self.myObject.myString;
seems like that's more self.C-V than M-V-C.
After messin' about on my own this works:
#import "myClass.h"
#implementation ViewController {
MyClass *myObject;
}
-(void)viewDidLoad {
myObject = [[myClass alloc] init]
}
.
.
.
-(void)someMethod{
displayLabel = myObject.myString;
my question is; is there any danger in using second example? or to ask differently, does it give the compiler an easier task of keeping MODEL separate from VIEW and CONTROLLER?
There are a couple of implementation details that are different between your two examples but they are essentially doing the exact same thing.
In both cases you are declaring a backing ivar. This line #property (nonatomic,strong) myClass *myobject; will implicitly #synthesize myObject = _myObject;, which is similar to you manually writing:
#interface ViewController : UIViewController {
MyClass *_myObject;
}
// or
#implementation ViewController {
MyClass *_myObject;
}
The only other difference is that #property (nonatomic,strong) myClass *myobject; will also create the accessor methods for you
- (void)setMyObject:(MyClass *)myObject;
- (MyObject *)myObject;
This is indeed still MVC but controllers that are subclasses of UIViewController always manage at least one view. The M component is your myObject instance. As in most diagrams the Controller sits there managing the communications between the V and M both of which the controller owns
Using singletons or holding the model as a property of the controller is also a question of lifetime. I prefer using singletons for helper classes (getting data from game center or fetching data from core data). Singletons are useful for things you need to access from different places within your app (where lifetime of your singleton is longer than your view controller's lifetime). But for view controller dependent models you can of course use them as properties of the view controller. Let's say you have ten view controllers in your app each displaying completely different content it does absolutely not make sense to keep all data for all possible view controllers in memory (in a singleton) just to have data ready in case the user wants to see any of the view controllers. In this case it's no shame to load your model's data from within your view controller implementation and hold it as a property. This guarantees that data is auto released when the view controller's lifetime ends and avoids conflicts. Holding data in singleton would make sense when you display data loaded from a server that does not need to be refreshed everytime you present your data to reduce the amount of traffic generated by loading the data. Using singletons might be dangerous regarding thread safety e.g. when data is mutated from a background thread while iterating over your datasource object to refresh a table view's content. Singletons can also lead to tigh coupling which should be avoided. Using an instance variable instead of a property is still a good choice if you want to hold a weak reference to an object as it is automatically set to nil if the referenced object gets auto released. A weak property would in this case lead to bad access.
Suppose I have several objects (models) that belong to a UIViewController. When one of the models changes or does something, I want it to notify the UIViewController so that the view can update immediately, or perform some animation in response.
How do I do this? Specifically,
(1.) How do the individual objects know about the ViewController? If I store a pointer to the ViewController inside each object, then I introduce a cyclical reference where a ViewController refers to children that refer to ViewController.
(2.) Even with a weak pointer, the model objects themselves don't know which view inside the ViewController corresponds to itself. For example, say there are 10 views and 10 models. The models aren't coupled with the views (i.e. each model has a view that represents it in the array of 10, but the index isn't always the same, since the user is constantly switching them around, so while the models have a stable index 0...9, the view representing model 0 could be at index 1, or later at index 2, 3, ..., 9), so when a model updates, it needs to tell the ViewController somehow which of the 10 views represents it, except it doesn't know. So the ViewController in turn can't know which View to update.
I should add that my current code just gives each model object a pointer to its corresponding View, so that it doesn't need to go through the ViewController to access the correct View and update its contents. But this would seem to violate MVC, so I'm trying to correct my code and realized I don't know how in this particular situation.
Sorry if that is confusing. I'll update the question if anything is unclear.
EDIT: Updated with more concrete example code.
#interface ViewController : UIViewController<ProfileViewControllerDelegate>
{
BattleModel* battle;
}
#property (nonatomic, strong) BattleModel* battle;
#property (nonatomic, weak) NSArray* heroPersons; // Person objects
#property (nonatomic, weak) NSArray* villainPersons; // Person objects
#property (nonatomic, strong) IBOutletCollection(PersonImageView) NSArray* villainImages;
#property (nonatomic, strong) IBOutletCollection(HealthBarView) NSArray* villainHealthBars;
#property (nonatomic, strong) IBOutletCollection(PersonImageView) NSArray* heroImages;
#property (nonatomic, strong) IBOutletCollection(HealthBarView) NSArray* heroHealthBars;
#property (weak, nonatomic) IBOutlet UITextView* reporter;
That describes the ViewController Interface. Now, each Person class handles modification of the Person, in addition to how the Person reacts to attacks.
#interface Person : NSObject
{
__weak Profile* profile;
__weak PersonImageView* personImg;
__weak BattleModel* currentBattle;
Reaction* currentReaction;
}
// Reaction happens in the following methods:
-(BOOL) targetedFoeWithMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(BOOL) wasTargetedByMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(void) hitFoeWithMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(void) wasHitByMove:(SelectedMove*)selMove log:(UITextView*)reporter;
As you can see, I currently violate MVC because the reporter is passed into the model Person, and also a pointer to the personImg, which the VC should handle. Anyways, as the battle happens, the Reaction* is queried inside the various targeted, hit routines, and that's where modifications happen, and the reporter is notified. Notice how the Person has NO IDEA whatsoever which index its corresponding PersonView is occupying in the grand scheme UIViewController, so when currentReaction triggers in some deep routine, and passes up the data, the ViewController has no idea which UIView to update. It knew at one point (when the Person was created), but the heroImages and villainImages array gets shifted around a lot (even switching sides if one Person goes from hero to villain), so the index it had at birth isn't the index it necessarily has at the point when a Reaction triggers and needs to update the PersonImageView. That's a bit of an annoyance, so I might need to have data passed back up to the UIViewController, telling it which PersonImageView just underwent a Reaction (how would I do that?).
Here is a sample Reaction, just for completeness and helping you understand the system:
-(void) wasHitByMove:(SelectedMove*)selMove log:(UITextView*)reporter
{
[self.currentReaction personWasHitByMove:selMove inBattle:self.currentBattle log:reporter];
}
And depending on if the Reaction is relevant (there are several subclasses of Reaction that do different things), a modification like "loseLife:(int)life" is called through a Person property setter, and that's when I need the report/animation to be bubbled up to the VC. Right now, the erroneous code (bad design) is simply going directly to the personImg for the animation, then the reporter for the textual notification. I realize this is bad design, which is why I'm here, but it isn't as simple as telling the VC through a callback, "(BOOL) Hey, something happened to me." because which PersonImageView is "me"? How does the ViewController, in the middle of its own method executing the logic of the battle, know to respond immediately to the callback? How is the callback even made? (Re: Delegates. So attach the delegate protocol to Person, PersonImageView, and UIViewController? And since they all follow the protocol, then they can interpret the common message through the protocol method?)
I also looked in NSNotification but find that too complicated for this purpose; it seems like it's more of a passive Observer type of thing, where in my application, when a Reaction triggers, I need the UIViewController to immediately update the screen with UITextView text + PersonImageView animations. Immediately in the sense that before the Reaction can trigger again, the animation needs to happen, as it could involve the PersonImageView recoiling, or falling down, or changing appearance. It isn't a real-time game, but the next attack will have already been queued and will be coming momentarily after the one before has triggered the currentReaction, so any kind of delay or passive observation might be bad.
Delegates.
Suppose you have a view with a button. Give it a protocol:
#protocol MyButtonViewDelegate
#required
- (void)buttonPressed;
#end
Now your view with a button has a property like this:
#property (nonatomic, weak) NSObject<MyButtonViewDelegate> *delegate;
Which is its reference to the view controller.
The view with a button's class will have a method like this which is hooked up to the button:
- (IBAction)buttonPressed:(id)sender {
[self.delegate buttonPressed];
}
Your view control conforms to the MyButtonViewDelegate protocol and sets itself as the delegate to the view. It then implements the method buttonPressed:
- (void)buttonPressed {
// do whatever you want to do when that button was pressed
}
If you need to pass a reference to the view in this method, you can do that too:
#protocol MyViewDelegate
#required
- (void)someMethod:(id)sender;
#end
Then:
- (IBAction)someUserInteractionWithView {
[self.delegate someMethod:self];
}
The delegate then implements the method from the protocol:
- (void)someMethod:(id)sender {
// sender is a reference to the view which called this method on delegate
}
You can write a protocol method to send as many arguments as you want. You can even have these methods return values that you make use of:
#protocol MyResizableViewProtocol
#required
- (CGRect)newSizeForResizableView:(MyResizableView*)resizableView;
#end
Something within the view class triggers this method to be called on the delegate and request a new size. The delegate implements this method:
- (CGRect)newSizeForResizableView:(MyResizableView*)resizableView {
return CGRectMake(0.0f, 0.0f, 100.0f, 400.0f);
}
And the resizable view makes use of the method as such:
- (void)timeToResize {
self.frame = [self.delegate newSizeForResizableView:self];
}
As far as I'm concerned, understanding protocols and delegates is one of the more important aspects of iOS development.
Bocks are a great way to implement callbacks in objective-c. Here is a link to Apple's documentation. Apple already implements blocks all over their APIs with NSOperationQueue, NSOperation, view animation, and many others. Check out this article about communication patterns in iOS, it explains the right circumstances for using delegation, blocks, notifications, and others.
There are several ways to do this. One way is by notifications. Have your models send out notifications when these interesting events happen. Your view controller can then sign up to receive these notifications and act accordingly.
In each model, do something like this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"somethingInModel3" object:self];
In the view controller, do something like this in init or viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleModel3Event:)
name:#"somethingInModel3"
object:nil];
The selector specifies the method that will handle the notification. You need to write that method as appropriate.
You'll also need to put (e.g., in dealloc in the view controller):
[[NSNotificationCenter defaultCenter] removeObserver:self];
I need some info about how to pass data between classes.. To be specific, I want to store in a class in an array some info, (using model store class), and then use it in another class..
You can do like:
For example: you want to pass Array from FirstViewController to SecondViewController.
Create Array in SecondViewController first and define it as property as in SecondViewController.h:
NSMutableArray *secondArr;
#property (nonatomic, retain) NSMutableArray *secondArr;
In SecondViewController.m:
#synthesize secondArr;
Then, for example, you want to pass Array when a button in FirstViewController is touched.
In its action (create IBAction, link it with the button's touchesUpInside), you can set it to get the instance of your second view controller, for example:
secondViewController.secondArr = firstArr;
I've posted some alternatives to the delegate pattern in Q&A What are alternatives to “delegates” for passing data between controllers?.
just declare array(let array2) in second class(MYSecondView) and do not forget to set his propert and synthesize.
and then go to first class in .h file and import class(#import "MySecondView") where you define array2.
create an object of second class(MySecondView *objMySecondView=[MySecondView alloc]init];) in your first class.
then pass value to array2 from array of first class.
like
objMySecondView.array2 setObjectFromArray=array1;
If you don't know how to do that, it's seems that you're at the very beginning of learning Objective-c and OO programming. So you better have some time to read about it
A good place to start is here