Purely by accident I discovered that calling [bar.view addSubview:[foo view]] doesn't work, but [bar.view addSubview:foo.view] does in the following code.
foo=[fooViewController alloc] initWithNibName:#"fooViewController" andBundle:nil];
[self.view addSubview:foo.view];//here's where I swap the two commands out
[foo aFunctionThatSendsAMessageToOneOfFoosSubViews];
(That last line is because foo has some sub-views that need to be set up prior to running -- notably, a UIWebView. If they haven't been instantiated before the message is sent, the message winds up going to nil. With foo.)
I thought these two were functionally identical -- that foo.view calls the same getter that [foo view] does, but in practice that's not the case; the dot syntax gets the desired results, while using the brackets winds up sending the message to nil.
If you'd asked me ten minutes ago, I would have told you the difference between the two expressions was 'syntax, and nothing else'. Given that I'm clearly wrong, I need to understand HOW I'm wrong or I'm going to stumble over it again.
They are functionally equivalent. I think this is a race condition. When you first call foo.view in that code, the view is not loaded yet, and a call is sent to [foo loadView]. You can't be sure that the view is loaded until [foo viewDidLoad] is called or foo.isViewLoaded == YES.
You need to wait make sure the view is loaded before performing any actions that rely on it, such as [foo aFunctionThatSendsAMessageToOneOfFoosSubViews].
In your current case, sometimes it is loading in time and sometimes it isn't.
if i'm not mistaken the problem is that [foo view] tries to call a method named view (and if you don't have it the return is nil)
on the other hand in the case of foo.view, view is a property of the class
Related
I'm familiar with the delegate pattern and nilling my delegates, especially when doing asynchronous calls which are still in progress when my view controllers disappear. I nil the delegate, and the callback successfully returns on a nil object.
I'm now experimenting with using completion blocks to make my code a little easier to read.
I call a network service from my view controller, and pass a block which updates my UITableView. Under normal circumstances it works fine. However, if I leave the view before it completes, the completion handler block is executed - but the UITableView is now a zombie.
Whats the usual pattern for handling this?
UPDATE WITH CODE SAMPLE
This is an iPad app, I have two view controllers on screen at once, like a split view. One is the detail, and the other is a grid of images. I click an image and it tell the detail to load the info. However, if i click the images too fast before they have chance to do the network call - I have the problems. On changing images the code below is called which counts the favourites of a image....
So here is my dilemma, if I use the code below - it works fine but it leaks in instruments if you switch images before the network responds.
If I remove the __block and pass in self, then it crashes with zombies.
I can't win... I'm sure i'm missing something fundamental about using blocks.
__block UITableView *theTable = [self.table retain];
__block IndexedDictionary *tableData = [self.descriptionKeyValues retain];
FavouritesController *favourites = [Container controllerWithClass:FavouritesController.class];
[favourites countFavouritesForPhoto:self.photo
completion:^(int favesCount) {
[tableData insertObject:[NSString stringWithFormat:#"%i", favesCount]
forKey:#"Favourites:" atIndex:1];
[theTable reloadData];
[tableData release];
[theTable release];
}];
Any tips? Thanks
SECOND UPDATE
I changed the way I loaded the favourites. Instead of the favourites being a singleton, I create an instance on each photo change. By replacing this and killing the old one - the block has nowhere to callback (i guess it doesn't even exist) and my code now just looks like the below, and it appear to be working:
[self.favourites countFavouritesForPhoto:self.photo
completion:^(int favesCount) {
[self.descriptionKeyValues insertObject:[NSString stringWithFormat:#"%i", favesCount]
forKey:#"Favourites:" atIndex:1];
[self.table reloadData];
}];
It doesn't leak, and doesn't appear to be crashing either.
I recommend you test that the tableview is not nil at the start of the block. It sounds like the tableview is properly discarded when its parent view goes off-screen, so after that point, no tableview operations are valid.
Retaining the UITableView within the block is a bad idea, because datasource/tableview updates can result in implicit method calls and notifications that will not be relevant if the tableview is not on-screen.
Block will retain any object that it references, except for those annotated with __block. If you want not to execute completion blocks at all, just make some property like isCancelled and check whether it is YES before calling completion block.
So you have a background operation which has to call back another object after it finishes and the object can be destroyed in the meantime. The crashes you describe happen when you have non retained references. The problem as you see is that the referred object goes away and the pointer is invalid. Usually, what you do is unregister the delegate inside the dealloc method so that the background task continues, and whenever it is ready to communicate the results back it says "Shoot, my callback object is nil", and at least it doesn't crash.
Still, handling manually weak references is tedious and error prone. You can forget to nil a delegate inside a dealloc method and it may go without notice for months before you encounter a situation where the code crashes.
If you are targeting iOS 5.0 I would read up upon ARC and the weak references it provides. If you don't want to use ARC, or need to target pre 5.x devices, I would recommend using zeroing weak reference libraries like MAZeroingWeakRef which work also for 3.x devices.
With either ARC's weak references or MAZeroingWeakRef, you would implement the background task with one of these fancy weak reference objects pointing back to your table. Now if the pointed object goes away, the weak pointer will nil itself and your background task won't crash.
I'm getting an strange case of excessive retain counts for a view controller that I'm loading when a button is pushed.
This is the code:
-(IBAction)new
{
if (!viewSpace)
viewSpace = [[ViewSpace alloc] initWithNibName:#"ViewSpace" bundle:nil];
viewSpace.delegate = self;
viewSpace.view.frame = CGRectMake(0, 0, viewSpace.view.frame.size.width, viewSpace.view.frame.size.height);
[self presentModalViewController:viewSpace animated:YES];
NSLog(#"Count Retain: %d",[viewSpace retainCount]);
}
-(void)viewSpaceWasDissmissed:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
[viewSpace release];
NSLog(#"Count Retain: %d",[viewSpace retainCount]);
}
When the IBAction New is executed first time, the retain count is 5 when just is created. (It must be 1).
When the ViewSpace object must be unload calls viewSpaceWasDismissed function in order to remove the modal view and release the previous object.
The problem is that never the retain count reach 0 and the dealloc method of ViewSpace never is called causing memory leaks.
My question is how is possible that a recently created ViewController have 5 retains? I made sure that is never created before.
Thanks.
Cocoa is probably retaining the view controller 4 times internally for reasons of its own. This isn't a problem.
More generally, the -retainCount method is useless for reasons like this, and you should never call it. It will not help you, and it will confuse you.
To debug your leak, I suggest using the leaks Instrument, inspecting the object, and analyzing where each retain and release is coming from to determine whether any are incorrect.
Check the documentation for -retainCount. I believe it says that you should not be calling it yourself - you just need to take care of any retains that you cause, and don't worry about the 'actual' retain count.
You're doing two things wrong here:
The Current view controller retains the modally presented view controller and releaseds it when it is dismissed. So you should release viewSpace after it is presented, and you don't need the release message in the dismissModalViewController method. As an aside ViewSpace is a poor name for a view controller. I had to read to the line where you are presenting it as a view controller before I knew it was a view controller. I think ViewSpaceController is a more descriptive name.
You are using retainCount which is always a bad idea. All that matters is that in your new method you created an owned object (with the alloc) and you balanced that ownership with a release (or at least you will do when you put in the correction I suggested in point 1) That's it. You took ownership of an object and you released it. The retainCount method tells you absolutely nothing that can be of any use to you. Don't do it. Just balance ownerships with release, and that is all that matters.
I'm not 100% sure of every count but here are some:
Instantiation - 1
NIB - 1+
Strong Properties (1+)
Additionally any properties that list it as a strong property (in ARC).
I noticed that when you launch a nib and you use components of the controller in the nib design, it will increase reference counts (in a strong manner) on the controller instance.
I am getting a crash the second time I attempt to add a certain view as a subview. The crash happens here:
-(void)AddAsScrollableSubContext:(UIView*)view {
[pExtendedScrollableSubContextBounds addSubview: view]; //CRASH HERE
pSubScroll.userInteractionEnabled = true;
}
the second time I call...
[mSongContext AddAsScrollableSubContext:pEQRoot];
The flow is something along the lines of
[mSongContext AddAsScrollableSubContext:pEQRoot];
...Load a lot of stuff
...Press a Button
...Unload a lot of stuff
[pEQRoot removeFromSuperview];
...Press a Button
[mSongContext AddAsScrollableSubContext:pEQRoot];
When I get the bad access the callstack looks like the following:
Both objects (pExtendedScrollableSubContextBounds and pEQRoot) appear to be valid. Adding other subview to pExtendedScrollableSubContextBounds works fine and calling other operations on pEQRoot (subview, frame) also work.
I read the in objsend r0 was the object and r1 was the selector so I looked at the memory address for r1 and saw...
This feels like I am trashing memory somewhere around isKindOfClass: but I am not quite sure. Could anyone point me to more info on iOS obj_msgsend? is there a way I can setup a watch point to catch when this memory trash is occurring?
Use NSZombies to fix the problem.
On a slightly unrelated note, there's a rule of thumb - NARC which stands for new, allocate, retain, copy. If a method call includes any of these keywords, then we have ownership of the object and we are then supposed to release the object.
I don't know if it's possible for me to include code here that's relevant as my project is so large but are there any typical reasons why NSLog would repeat some warnings and calls to it at occasions where only one call/error is occuring?
As an example, I have a subclass of NSBox that inits an instance of another class on awakeFromNib:
- (void) awakeFromNib {
burbControllerInstance = [[BurbController alloc] init];
if (burbControllerInstance) {
NSLog(#"init ok");
}
}
I get NSLog printing "init ok" twice. I don't see why this subclass would be 'awoken' twice anywhere in my project. This is part of a larger problem where I can't get variables to return anything but nil from the class I'm creating an instance of. I'm wondering if perhaps the double values are something to do with it.
This post could be helpful, i. e. one comment:
Also important: awakeFromNib can be
called multiple times on the
controller if you use the same
controller for several nibs – say,
you’re using the app delegate as the
owner of both the app’s About Box and
preferences dialog. So you’ll need an
extra guard test if you use
awakeFromNib for anything but
initializing the nib objects
Update: Much more interesting could also be this, where the author mentions that awakeFromNib gets called twice. Unfortunately there is no real answer for this particular problem but maybe some basic ideas.
Update #2: Another potential solution from stackoverflow.com: View Controller calls awakeFromNib twice.
And thank you in advance for your attention.
I am trying to achieve a little animation effect in my simple application, using this time an UIPickerView object.
Basically, when a certain event occurs, I wish my picker view to animate itself selecting its last row (with animation, of course) and immediately after (without any further user interaction) its first row (with animation, of course). This, to give an 'application got crazy' effect which is supposed to be really really witty, I guess.
Therefore my code was:
[myPicker selectRow:[myPicker numberOfRowsInComponent:0]-1 inComponent:0 animated:YES];
[myPicker selectRow: 0 inComponent:0 animated:YES];
And what I got is that the picker will execute only the second instruction no matter the row specified within it. It seems to ignore the first one.
I thought it could be for some 'clever' function of the compiler which notes that two following instructions perform an assignment to the same variable, therefore is better to ignore the first one (not considering the 'ANIMATED' factor).
I don't know if it is the case but I tried to move these two instructions within two if statements based on value of some parameter, trying this way to fool the compiler (which cannot know at compile time the value of that parameters, therefore it is not supposed to perform such an optimization).
It didn't work, either because I am completely out of trace or because the compiler is far more clever than me.
I even thought it could be a problem of delays and correct sequence of events therefore I tried to rewrite the code as follow
-(void) setPickerRowToLastRow;
{
[myPicker selectRow:[myPicker numberOfRowsInComponent:0]-1 inComponent:0 animated:YES];
}
-(void) setPickerRowToFirstRow;
{
[myPicker selectRow:0 inComponent:0 animated:YES];
}
.....
[self performSelector: #selector(setPickerRowToLastRow)
withObject: nil
afterDelay: 1];
[self performSelector: #selector(setPickerRowToFirstRow)
withObject: nil
afterDelay: 1];
And it didn't work: the picker still performs just the second action and it will move just once.
And now the question(s):
Why does the picker move just once?
How can I reach my goal (even if maybe a bit trivial)?
This has nothing to do with the compiler. It's not that clever.
The reason only one animation is performed is that the animation doesn't start "at once" but on the next run loop (more about which here). At that point the animation will start, using the latest values sent to the picker.
Using performSelector:withObject:afterDelay: should work, and the reason it doesn't is probably that you put 1 as the delay for both invocations. Set it to 0 on the first and 2 (for example) on the second, and it should work.