I have two table table controllers A and B.
A has a list of items and when an item is clicked, it pushes to B.
The problem is that when B was shown, the instrutment indicated a live memory increase, but it did not decreased when I clicked on back button.
The dealloc method in B was executed but the memory seemed changed slightly.
I checked that B was not retained anywhere else so what may the reasons that can cause this problem?
In B, there are many textfields, labels which are nonatomic and strong. I draw them in codes rather than using xibs. If I commented the codes for initializing them and adding them to the table header view, then the problem is gone.
I use Arc and simulator 6.1 by the way.
It seems to me that some of the textfields/labels inside B might have some strong reference to B itself making it impossible for arc to release them cause B has a strong pointer to and element inside it and this element has a strong pointer to B.
As far as I know thats not the default behaviour of textfields/labels, but maybe you've subclassed them...
Hard to try anything other than guessing with that little amount of information you gave
Do you need the views to be accessible in the view controller context? Can you not assign a tag to them and retrieve them from the tableview header when you need to change them?
Related
A while ago I became a fan of creating/manipulating the view hierarchy through code. Both because I think is more expressive and forces me to learn more about Cocoa.
But I just wrote a 5 lines of code which would look way more expressive if an IBOulet was used. It finds a subview within a view with a specific tag and sends a message to it. But I could easily create an IBOutlet and do it in one line only.
Because of this, I ask: is creating an IBOutlet too expensive?
P.S.: Let's cut off the "readability over performance" for now. I really want to know the impact of this.
IBOutlet is a marker for Xcode that gets removed by the time the preprocessing step is over. Internally, setting it up boils down to assigning a single pointer to an instance variable that "backs" the IBOutlet property. This pointer is assigned at the time the view is set up, and does not change after that. It is very cheap.
Finding a subview by tag, on the other hand, is a run-time operation that needs to run every time that you are looking for the subview. Usually it is cheap, but it may become considerably more expensive in a view with large number of subviews that have subviews as well.
Therefore, I would definitely go for IBOutlet, because it's a one-time deal, and because it lets you shorten the code from five lines to one.
You have to be able to find out the pros and cons of your current approach. In fact Apple's documentation states:
As with any item of object state, you should be able to justify its inclusion in a class; the more outlets an object has, the more memory it takes up. If there are other ways to obtain a reference to an object, such as finding it through its index position in a matrix, or through its inclusion as a function parameter, or through use of a tag (an assigned numeric identifier), you should do that instead.
I prefer to use IBOutlets in most cases, as (I know that you don't want to hear this) they make the code more readable and of course as #dasblinkenlight pointed out, your traversing of the view hierarchy is performed at runtime and therefore anytime (assuming you do this in viewDidLoad) the view is loaded.
My recommendation: Stick to IBOutlets, unless you have a really really simple view hierarchy and using tags to find your subviews is the best solution.
SUMMARY BEFORE EXPLANATION
It’s as if the storyboard is treating the Tables as pointers to a single instance, simply because I copied and pasted through the storyboard. I am recording this here in part as a reminder to myself (I am an avid user of Stack Overflow) and as a warning to others. And of course, I want to know if there is a simple way of truly disentangling the two without having to recreate one of the ViewControllers from scratch. please, read on for explanation.
BACK STORY
First I created CatUIViewController using the storyboard. I completed the whole thing along with .h and .m files. Then, needing a similar ViewController for Dogs, I dragged and dropped an empty ViewController onto the storyboard from Object Library, and then I copied and pasted the entire UITableView from CatUIViewController to DogUIViewController. Then of course I did the same with the .h and .m files. Really the only difference between CatUIViewController and DogUIViewController is that Cat has two buttons in it’s header while dog has an UIImageView, along with other data that both headers share.
Naturally, Xcode does all the connections for me the moment I pasted into the Dog equivalents. It wired all the IBOutlets and IBActions automatically: a nice feature of Xcode.
FURTHER DETAIL
Both table views have a background image. And navigation from Cat to Dog is possible because my app is using UITabBar — no other linking exists between the two. Furthermore, while Cat leads its Tab, Dog is way down the line in its Tab’s NavigationController.
THE PROBLEM
The problem is this: If I am in DogUIViewController and click the tab to transition to CatUIViewController, after the transition the background of DogUIViewController shows up as the background of CatUIViewController. In fact, if I visit DogUIViewController and then navigate to any other page so ever… the second I click on the Tab for CatUIViewController, mysteriously it first shows the data for DogUIViewController before switching to the correct data about 1 second later. Another strangeness is that the converse is equally true.
I have been investigating the code to see what might be the cause and so far I have found nothing. It’s as if the entanglement is happening at a layer in Xcode that is not exposed to me: I review the Connections Inspector, the .h and .m files and I cannot find anything so far. I am hoping it is a simple oversight on my part so I keep digging. But just in case it is not and someone else has resolved something similar, I am here asking.
Whereas you copied and pasted not only the UITableView but also both the .h and the .m file, the possibility for errors abounds. But of particular interest to me is that you mention differences to exist exclusively in the header files. This to me means the likely cause of errors is your data source. Wherever you are fetching the data, it takes a split second so that the new data is delayed just enough for the old data to show up. So I am ending my response with a question: How are you fetching your data? Are you caching the data? Are you cleaning property? For example, if you are using notification for the server to update your view controllers, viewDidUnload is deprecated so I wouldn't use that to unregister notificatoins. In whatever the case, you at your data source.
I have no idea if I'm using the right terminology here, but I'm hoping someone can help - I'm pretty new to iOS Dev and have run into a problem:
I have created a custom class that is used to store an object. It has a bunch of properties and functions, just as an object should. This object is being declared in my ViewController's .h file, and initialised and used throughout the ViewController. The object holds a bunch of information about a test, and takes some measurements in various threads.
The problem I'm facing is that when I load another view (using ECSlidingViewController for the menu) and then return to the view which had the object...it seems to have forgotten the object. The tasks running in the threads are all still running, but that instance of the object seems to be gone.
Is there a way to preserve an instance of the object when changing views so that when I return to the appropriate view, the object is still there and I can still use it?
Thanks!
I'd recommend you to put the objects reference in a transversal class, something like a manager or any other class which you consider appropriate according to your design and most important it needs to be a class that you're completely sure won't be release nor re-created as view controllers are usually when you change from one view controller to the other.
A singleton instance that manages your main logic could be a good option.
Turns out that my menuViewController that was implementing the ECSlidingViewController was recreating the view that contained the object each time I selected the item from the menu. Thanks #rdelmar for pointing this out!
The object was persisting, but in a viewController that was being replaced each time it was selected from the menu and hence the object was unreachable.
I simply implemented this and references to the viewControllers are now being stored in a mutable dictionary and simply recalled when the view is needed, rather than recreating the view. This means that the viewController is being reused and the object is persisting with it.
I have an app that displays the nodes of a connected graph. When the user touches a node I highlight the paths to the adjacent nodes by showing a clearColor "connections" view behind the nodes that is sized to contain the nodes of interest. This view contains child views for each path and each of these path views has a drawRect method that draws a single line that appears behind the source node and each adjacent node. All well and good and this approach opens the potential for doing interesting things with the paths in the future.
However, I recently added code to detect an external display with the idea that the main display would be a zoomed in view inside a scroll view and the external display would show the global view of all nodes. It worked fine until I added the connection view to the external display to show what was selected by touch on the main display. My path views (and their parent and grand parent views) are identical as far as I can tell, never have their drawRect methods called, as do their main view cousins, after setNeedsDisplay has been called. They are both initialized in the same place and in the same way.
I've tried dozens (hundreds?) of things to isolate the problem, but haven't figured out the problem. If I add a generic UIView to the connections view it appears in both places, but my path view, which extends UIView by adding a drawRect, only gets called on my main display and never on my external display.
I've run out of ideas. Does anyone have any ideas on what might be the cause of the problem?
I was pulling hair trying to figure this one out. Comparing the state of the two views did not show any difference. A few days ago I finally decided to write some a python routine to run in LLDB that would dump the state of the objects and then did a diff on the two views. It turns out that they were attempting to show the same subviews!
In order to minimize object creation I was stuffing the path subviews into an array to avoid creating more than I ever needed and hiding them when not in use. But, I had stupidly declared this array outside of the #interface block and had inadvertently made it into a global and so both views were using the last set of subviews defined. Of course, a view cannot be a child of more than one parent view. Moving the declaration into the right place fixed the problem.
Dumb error! But perhaps this will jog something for someone else with a similar problem.
Part of the app I'm working on involves a UIPageViewController, where each page displays an 'entry' that is stored in Core Data. An entry includes, among other things, some images that are being compressed and stored as NSData. Thus, to load these images and display them on a page, I'm using imageWithData, i.e.
photo.image = [UIImage imageWithData:entry.photo];
The problem is that imageWithData is not particularly quick, and so flipping through pages is not as responsive as I would like. My best attempt at remedying this situation has been to preload a number of the view controllers that are displayed by my UIPageViewController into an array. (Not sure if that's the best thing to do, but there you have it)
So, to clarify, I have a navigation controller, which contains viewControllerA, which then links to viewControllerB - which displays the UIPageView and the entryControllers (one entryController on each page). The problem is that when I use the navigation bar to go back from viewControllerB to viewControllerA, I want viewControllerB's array of entryControllers to be released from memory. But, ARC doesn't seem to be doing so. Therefore, after going back and forth between viewControllerA and viewControllerB a few times, turning a few pages each time, I start to get memory warnings - which ends up clearing the current array of entryControllers, and defeats the purpose of having that array, since the entries then have to be reloaded every time I get a memory warning.
In short, ARC isn't clearing any of the memory I've allocated for viewControllerB when I go back to viewControllerA via my navigation controller. I don't like that. If anyone could suggest a reason that this is happening, or let me know if I'm going about this whole thing the wrong way, it would be hugely appreciated. I'm just trying to speed up the transition from one page to the next!
Thanks a bunch.
The only reason for ARC to not release your memory is that you still are retaining it somewhere. Only you know where that is.
Usually, this is caused by a retain cycle, which can happen easily with blocks.
However, it is just as likely that you are keeping a pointer to the object someplace, in a controller, an iVar, or some collection.
Have you run the static analyzer on your code? It's pretty good at finding some of those things.
What does instruments tell you? It's got some neat tools for finding leaks and retain cycles.
EDIT
You can have as many references to any object that you want. Once the count goes to zero, it will be released.
The problem is that you are holding an array of strong pointers to view controllers as a cache. If you want them released, you have to manually nil those pointers. You are somehow pre-loading controllers into an array. They will stay alive until you unload them.
So, as long as you have a strong pointer to something, it will stay around.
Jody's edit, specifically mentioning setting pointers to nil, is what got the ball rolling for me in terms of understanding, and in the end I was able to figure out my problem by setting certain pointers to nil when my "viewControllerB" disappeared. Along the way, however, I also realized another important point - large images in core data are a huge memory burden, and in iOS 5, this can be remedied by setting an option to allow for external storage. So for anyone else who's a relative beginner like me and might come across a similar memory problem, this is a key way to limit the amount of memory being eaten by managed objects storing large amounts of data - your images (or whatever large amount of data) are saved to file automatically, without you having to do anything different in code.