i had several questions about objective-c, could you help me understand it? Here they are :
why do we use sometimes :
self.myView.delegate = self; ?
There is the same thing with the Ipad and Splitview : why do we use the delegate explicitly like this :
uisplitviewController *svc = ...
svc.delegate = pvc.otherViewController;
For now, i understand the "appDelegate", but what about this delegate method?
2.I saw several times the use of another "viewController" as below, instead of "allocating" directly the "mainViewControler", why do we use this intermediate?
MainViewController *viewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
self.mainViewController = viewController;
[viewController release];
3.Following some tutorials from appsmuck.com, i saw that they use :
"(void)loadFlipsideViewController "
but no "loadView", so, can we replace the "View" with the controller view?
4.Finally, i can switch from "iphone" Simulator to "ipad" simulator, but i always get the ipad simulator each time i build the projet, is there a way to always let the "iphone" simulator to be the simulator by default?
And that's it. :) Thanks for your help
Paul
1 . Delegation Design Pattern
The delegation design pattern is a way
of modifying complex objects without
subclassing them. Instead of
subclassing, you use the complex
object as is and put any custom code
for modifying the behavior of that
object inside a separate object, which
is referred to as the delegate object.
At predefined times, the complex
object then calls the methods of the
delegate object to give it a chance to
run its custom code.
2 . The reason for creating another viewController is for memory management. It can be done in one line but then you will be needlessly adding an object to the autorelease pool.
//Now the autorelease pool has to track this object
self.mainViewController = [[[MainViewController alloc] initWithNibName:#"MainView" bundle:nil] autorelease];
3 . -(void)loadView is inherited from UIViewController so no you can not just change it to loadViewController because that will just create a custom method. That is exactly what (void)loadFlipsideViewController is trying to accomplish and by the name of it it should load a new view controller and display it using a flip animation.
4 . In XCode 4 you need to set the scheme to the correct simulator.
First of all, it's going to me MUCH easier to get answers if you break all these queries up into separate questions. You're going to have a hard time picking an answer that answers them all best.
That said, I'll give #1 a shot.
Many types of objects in Cocoa (and Cocoa Touch) send messages. Some, like NSFetchedResultsController send messages when their contents change. Some, like UITableViewController, send a message when a table cell is touched. These messages have to GO somewhere. They can't be sent just "out there" or nothing will ever hear them. These messages need a destination. In Cocoa, the destination for these messages is called the "delegate." As in, "I designate this object to be my delegate, and receive my messages."
If you are in a viewController that is controlling a UITableView, very often is makes sense to simply specify "self" as the delegate. That is saying, in effect, hey, Mr. UITableView, just send your messages to me, I'll handle them. In turn, your viewController must declare (in the .h) that they conform to the UITableViewDelegate protocol, and then the required methods in that protocol must be implemented in your .m.
This is a VERY common pattern in Cocoa. Don't proceed until your understand it.
For delegate you can check use of Delegate in iphone sdk. I have a long answer there.
#property (nonatomic, retain) MyClass *obj;
MyClass *tmpObj = [[MyClass alloc] init];
self.obj = tmpObj;
[tmpObj release];
Let's see what happens here. [MyClass alloc] allocates an object and you have retain count of 1. In the next line you are calling the setter which also retains. So you are increasing the retain count. Naturally you will release this property later, may be in dealloc. So now you are owner of this object 2 times. To match the first ownership via alloc you release in the 3rd line.
Now see what happens if you do this in one line:
self.obj = [[MyClass alloc] init];
You have gained ownership here two times, one through alloc and one through retain property. So you need to write [obj release] twice. Otherwise you will have a memory leak. But releasing twice is confusing and easier to create bug and you should never do this. Or you need to autorelease this in the same line which is also confusing.
So the summary is if you have retain property, then write three lines of code to handle with double ownership.
Sorry, can't help with other parts.
Related
I've found a couple of sources that explain how to use the AppDelegate to share data between objects in an iOS application. I've implemented it quite painlessly and it looks like a good approach in my situation. Thinking about what could be done using the AppDelegate, I am wondering where the line should be drawn.
Obviously there are other ways to share data across view controllers, use Singleton objects, and NSUserDefaults. When is it appropriate to use the AppDelegate to share data? In my current situation, I use this approach to store the appleDeviceToken used for push notifications. I use that token when users login or logout of the app.
In MyAppDelegate.h I declare the property:
#property (nonatomic, retain) NSString *appleDeviceToken;
In MyAppDelegate.m I synthesize appleDeviceToken and then set it:
#synthesize appleDeviceToken;
------------------------------------------------------
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString *devToken = [[[[deviceToken description]
stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
appleDeviceToken = devToken;
}
Then, in my LoginViewController.m I retrieve it and post it to my server:
NSString *urlForDeviceTokenPost = [NSString stringWithFormat: #"/api/users/%i/appleDeviceToken", userId];
MyAppDelegate *appDelegate = (MyAppDelegate*) [UIApplication sharedApplication].delegate;
NSString *appleDeviceTokenStr = appDelegate.appleDeviceToken;
AppleDeviceToken *appleDeviceToken = [[AppleDeviceToken alloc] init];
appleDeviceToken.deviceToken = appleDeviceTokenStr;
[[RKObjectManager sharedManager] postObject:appleDeviceToken delegate:self];
This works great so far, but is this the ideal approach? What else should I know?
When the data and objects are truly global and/or cannot be pushed further down the graph. Storage at this high level is usually not required. As well, your implementations should usually have little to no knowledge about the app delegate -- What's worse than a Singleton? The God-Singleton :) If the app delegate is complex, something's gone wrong. If the app delegate's interface is visible (via #import) to many of your implementations or they message it directly, then something is wrong.
There is no need for an (idiomatic ObjC) singleton -- there is one instance of the app delegate.
NSUserDefaults is for persistence of small values (as the name implies) -- the ability to share is a side effect.
Since the data is already sent into the app delegate by UIKit in this case, that may be a fine place to store the data, or an object representation of. You might also consider forwarding those messages to an appropriate handler. The important point -- In most cases, you would want initialization to flow down the object graph, and to flow from the lowest points possible (e.g. as opposed to many objects referring back to the app delegate). So you might see the app delegate set a top-level view controller's model, but the view controller can then set the models of the view controllers it pushes. This way you will reduce dependencies and control flow, cause and effect will be easier to trace, and you will be able to test it more easily -- free of the context of a massive global state.
The following line always indicates that you've done something wrong:
MyAppDelegate *appDelegate = (MyAppDelegate*) [UIApplication sharedApplication].delegate;
The application delegate is the delegate of the UIApplication. It is called that for a reason. It is not called the ApplicationDataStore or even the ApplicationCoordinator. The fact that you're asking the application for its delegate and then treating it as something other than id<UIApplicationDelegate> means that you've asked it to do something it isn't tasked with doing. It's tasked with managing the things UIApplication needs (which doesn't mean "everything the 'app' needs).
It appears that you've already built a place to store this information: RKObjectManager. I would have the app delegate pass the token there, and I'd have the login view controller just note that it is time to push it. I wouldn't even put the string #"/api/users/%i/appleDeviceToken" in the view controller. That's not related to displaying the view. That's something for you networking stack (which you seem to have housed in RKObjectManager). "ViewController" means "controller for helping display the view" not "processor of the operation that the view represents."
That seems like an appropriate use. The application delegate is tempting to misuse because it's an easily-accessible object that's already present in every app. It has a real purpose, though, which is, as its title indicates, to make decisions for the application object, just as a table view delegate does for its table view object.
In this case, you're storing information that was passed to the delegate from the application itself. I'd say that's where the line is drawn.
Storing this token seems to be in accordance with the app delegate's purpose, unless you had another controller object which was focussed on dealing with remote notifications. In that case, you would probably just pass the token right on to that controller.
I'm more pragmatic. Since the appDelegate in my app knows how the tabBarController was populated, and all the navigation controllers, I have several methods there that let some arbitrary class communicate with some other class - usually these are single instances of some class (but not singletons). That said, if what you want to put there does not have a compelling reason to be in the appDelegate, well, it probably doesn't belong there!
I have ParentViewController that allocates ChildViewController, pushes it onto controller stack and releases it.
ChildViewController implements the protocol ProductDownloadDelegateProtocol required by the Product class.
At some point, ChildViewController creates a Product object and sets itself as its downloadDelegate.
While downloading, Product class updates ChildViewController via methods defined in ProductDownloadDelegateProtocol.
If the user presses the back button in the navBar of ChildViewController while downloading, the next update of download percentage from Product causes an EXC_BAD_ACCESS.
Although Product checks if downloadDelegate is nil, problem still occurs since ChildViewController/downloadDelegate is deallocated, but not set as nil. I don't know which point is best to set ChildViewController to nil.
Is my design wrong?
If your ChildViewController creates an instance of Product and sets itself as the delegate, it should be its responsibility to remove itself as the delegate when it's about to be unloaded. Either in it's viewDidUnload or dealloc method you should be setting the Product delegate to nil.
If ChildViewController stays around (say you are reusing the view controller), maybe you can remove it as the delegate in the viewWillDissappear method.
Another solution to fix this particular EXC_BAD_ACCESS issue is to move to ARC and use Zeroing Weak References (see a good writeup here, http://www.mikeash.com/pyblog/friday-qa-2010-07-16-zeroing-weak-references-in-objective-c.html).
Still, I would recommend you move to ARC for the right reasons, and not to fix this particular issue you are facing.
My rule of thumb goes like this: You should never be the delegate of an object you do not own. Here, "own" means "hold a strong reference to" (in ARC terms). The main exception is when the delegate retains you, and UIApplication.delegate because that's a bit weird.
Usually I bundle the logic into the setter like this:
-(void)setProduct:(Product*)p
{
product.delegate = nil;
[product release];
product = [p retain];
product.delegate = self;
}
-(void)dealloc
{
self.product = nil;
}
However, an underlying problem with this design is that a "product download" can only have one delegate. What if you navigate away from the ChildViewController and then back into (a new instance of) it? Are they different Product instances, both being downloaded?
A better way might be to have a download manager singleton (as much as I hate singletons) that manages downloads and uses NSNotification/NSNotificationCenter for progress notifications.
Im trying to add an object to a NSMutableArray in another xib. But seems isnt working. What im doing wrong?
Thanks!
-(void) buy {
CartViewController *carrinho = [[CartViewController alloc] initWithNibName:#"CartViewController" bundle:[NSBundle mainBundle]];
carrinho.produtoCodigo = [[NSMutableArray alloc]init];
[carrinho.produtoCodigo addObject:#"aa"];
[carrinho release];
NSLog(#"did");
}
Your code looks fine so far. Make sure the connections in InterfaceBuilder and the File's owner in the XIB is set correctly.
Ok, several things. First you don't need to pass in [NSBundle mainBundle]. nil works fine if you want the main bundle. Second issue is, produtoCodigo should be a property set to retain and as such you should pass in an autoreleased NSMutableArray i.e. [NSMutableArray array].
Thirdly, I would question why you would want to do this. It seems like a bad design. Ideally the mutable array should be an internal ivar in CartViewController. You should then have a method on CartViewController to handle the item. You should not care about how it is stored internally, only that you want to add an object to the controller.
If you want to pass in multiple objects you should have a method that takes an array of objects and pass that in.
Now finally, nibs don't really hold arrays, the class does. As such it shouldn't be an issue with your nib. The issue should therefore be with the class. Where are you checking whether the array is being updated and finding that it isn't?
you declare and create carrinho as a view controller, which should allocate and init the carrinho.produtoCodigo as well, if you have it synthesized. Then you alloc it again, which may be a memory leak. After adding the aa, you release it. Therefore, overall, you haven't accomplished anything. That mutable array comes into being, is modified, and then destroyed.
You mention "another xib" and from the name CartController and method name "buy" it sounds like you want to update a shopping cart that is being held by some other class. Therefore, the view or class with the cart (and mutable array) needs to be modified. It's like if you and a friend go shopping, and you delegate the job of managing the cart to him. He is the delegate, and only he can put stuff in the cart. Just because you want to buy something, you have to give it to him first so that he can put it in the cart. Right now, your code is like you grab something off the rack, but then put it back on the rack. It never makes it into the cart.
What you want to do is create a shopping protocol with a message addToCart which is what this code would instead do. It would send the message to the delegate to add the item to the cart. The other xib code has a method -(void)addToCart:(id)item; which is what is invoked when this code chunk does to call to the delegate. Look up protocols and delegates; they are simple to create, and the only way to get multiple controllers talking to one another.
Maybe you inserted this below code in the second XIB:
-(void) viewDidLoad {
produtoCodigo = [[NSMutableArray alloc] init];
}
because if you make allocate an array again, the previous objects in it will be removed.
I kind of understand why I'm getting this analyzer warning. Because I'm using an object that is being passed in. I've tried autorelease and retain however these cause me other problems like unrecognized selector sent to instance.
The aim of my CommonUI function is to re-use code, but I have to cater for addSubView and presentModalViewController.
Perhaps I'm doing some obvious wrong ?
Change your code like this:
HelpViewController *helpvc = [[HelpViewController alloc] init....];
[vw addSubview:helpvc.view];
[helpcv release];
I think you don't need to pass the other VC.
There are two problems here.
First, if you call [vc release] (as the other answers suggest), you'll certainly make the analyzer happy but likely crash the app. A view controller's view doesn't retain the controller, so any button targets in the view will be pointing to garbage.
You will need to somehow keep the HelpViewController retained for as long as it is showing up onscreen. The "parent" view controller should likely retain it somehow. You could autorelease it, and return it. Then whomever calls showHelpClick... would retain the returned controller.
Second, you don't need to have the (UIViewController *)vc passed in as an argument.
In a UIViewController subclass I create a bar button item that fires an event up the responder chain:
UIBarButtonItem* editListsButton = [[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStyleBordered target:nil action:#selector(edit)];
self.navigationItem.leftBarButtonItem = editListsButton;
[editListsButton release];
In an ancestor UIViewController subclass's implementation (i.e. in the .m file) I have the following:
#interface GroupController (PrivateMethods)
- (void) edit;
#end
- (void) edit {
... do something here ...
}
And of course in the corresponding .h file I do not declare the edit method. This was a random mistake on my part.
Should this work reliably anyways? What is the requirement for how to declare the method so that it receives the edit events?
BTW, I have reports that touching the "Edit" bar button item causes the app to crash every time it is touched, but only from a few of many thousands of users. I can't reproduce it.
There is no "visibility" for Objective-C methods beyond where you stick the declaration at compile time. At runtime, they are all the same.
First, action methods take an argument -- the sender. Thus, your method really should be declared as:
- (IBAction)edit:(id)sender;
Note that IBAction is actually #defined to be void. It is used by Interface Builder only. Since you are doing things programmatically, you could use void. Of course, that begs the question as to why you are doing things programmatically since that is almost always a waste of time, but... beyond the scope of this question.
In any case, yes, it should work reliably. Whether or not a method is declared in a header makes zero difference at runtime.
Given that your crashes are rather intermittent, it sounds more like you might have a memory management problem (or other latent crasher). Did you build-and-analyze your code? Got a crash log?