I have a UIScrollView full of tacos.
I attached a pull-to-refresh handler to it via: https://github.com/samvermette/SVPullToRefresh
It extends uiscrollview, and exposes this method:
[scrollview addInfiniteScrollingWithActionHandler:^{
// Get me more tacos
}];
When InfiniteScrolling is triggered, I clear the scrollview's subviews and data array(intentionally) and replace it with a new set.
It works great the first time. However, when I want to load more tacos it crashes.
I get:
-[SVInfiniteScrollingView retain]: message sent to deallocated instance 0x1e5db5d0
Not surprisingly, if I leave 1 subview left in my UIScrollview, everything works fine.
Question: How can I fix this?
I thought about declaring my properties with a strong pointer, like:
#property (strong, nonatomic) IBOutlet tacoScroller *tacoScroller;
But, I worry about a retain cycle & it also doesn't work.
Any help would be appreciated, perhaps I'm missing something fundamental.
Edit:
I'm using ARC
Use an UITableView to show your tacos, this way you will reuse views and avoid wasting memory. Also it is the easiest and most convenient way to show a list of things.
One simplest solution according to your description is simply add an empty & hidden subview inside the scrollview, i am sure it wont occupy much amount of memory.
I think you are invoking the wrong method. Infinite scrolling is for other purposes.
You probably want to use
[scrollView addPullToRefreshWithActionHandler:^{ ... }];
Also, as others pointed out, you definitely should consider to use a UITableView to present your data, which seems to be very suitable for the task.
Related
I guess i might not understand how memory deallocates in Swift properly and thats why i wanted to ask this:
If i create a Message to the user using a UIView with a Label. i show it with an animation and then use ".removeFromSuperview()". when does it's memory deallocates? what are the conditions for it to deallocate?
Im asking because if it doesn't deallocate until the app closes - it means that for the run of the app - each message shown and then hidden will take up memory for no good reason.
Thanks for anyone who explains :)
Views maintain a strong reference to their subviews. Once the subview is removed, the superview relinquishes this reference. If you have no other strong references the retain count will decrement to zero and the view will be released.
Recently I wrote some code where I tried to refer to an outlet on a UIViewController I'd just instantiated with [storyboard instantiateViewControllerWithIdentifier] and modify the subview that the outlet pointed to before presenting the ViewController. It didn't work because the ViewController's view hadn't loaded its subviews yet, including the one that my outlet referred to, so the property just gave me a null pointer.
After (with some struggle) tracking down the cause of my issue in the debugger, I Googled around and learned, through answers like this one, that I can cause the view to load its subviews without being displayed by calling the myViewController.view getter. After that, I can access my outlet without any problems.
It's a clear hack, though, and Xcode - quite rightly - doesn't like it, and angrily protests with this warning:
Property access result unused - getters should not be used for side effects
Is there a non-hacky alternative way to do this that doesn't involved abusing the .view getter? Alternatively, are there canonical/idiomatic patterns for this scenario involving something like dynamically adding a handler to be called as soon as the subviews are loaded?
Or is the standard solution just to replace myViewController.view with [myViewController view] to shut up Xcode's warning, and then live with the hack?
On iOS 9 or newer, one can use:
viewController.loadViewIfNeeded()
Docs: https://developer.apple.com/reference/uikit/uiviewcontroller/1621446-loadviewifneeded
I agree that forcing a view to load should be avoided but I ran into a case where it seemed the only reasonable solution to a problem (popping a UINavigationController containing a UISearchController that had yet to be invoked causes a nasty console says warning).
What I did was use new iOS9 API loadViewIfNeeded and for pre-iOS9 used viewController.view.alpha = 1.0. Of course a good comment above this code will prevent you (or someone else) removing this code later thinking it is unneeded.
The fact that Apple is now providing this API signals it can be needed from time to time.
Not sure how much cleaner this way, but it still works fine:
_ = vc.view
UPD: for your convenience, you can declare extension like below:
extension UIViewController {
func preloadView() {
let _ = view
}
}
You can read explaination by following URL: https://www.natashatherobot.com/ios-testing-view-controllers-swift/
merged Rudolph/Swany answers for pre ios9 deployment targets
if #available(iOS 9.0, *) {
loadViewIfNeeded()
}
else {
// _ = self.view works but some Swift compiler genius could optimize what seems like a noop out
// hence this perversion from this recipe http://stackoverflow.com/questions/17279604/clean-way-to-force-view-to-load-subviews-early
view.alpha = 1
}
If I understand you correctly, I think there's another fairly standard solution: move the outlet modification/configuration code into a viewDidLoad method (of the recently instantiated VC).
The topic is also discussed in this question.
It would require some restructuring, but it might give you a "cleaner" design in terms of MVC if your incoming VC handled its own configuration, and it would avoid the "You should never call this method directly" stricture on loadView.
You can call [myViewController loadView] to explicitly load the view, instead of abusing the .view getter. The .view getter actually calls loadView if necessary when called.
It's still not a very nice solution, since the UIView Documentation's section on loadView explicitly instructs that
You should never call this method directly
When creating UIs in IB I have often used buttons that have an action but there is no reason for the view controller to access this button.
The button text never changes, the button image never changes, it never moves, etc...
Because of this I don't give it an IBOutlet property and I don't connect it up to anything in the VC (other than the action of course).
There is a similar question on SO that I've read and the arguments on there go into memory management issues. That question is from early 2011, before ARC. Given that all my IBOutlet properties are weak anyway the memory is dealt with by their superview not by the view controller. So the issues mentioned in that question are now moot.
Is there a reason to connect them up now? Should they always have a connection? If so, why?
Short answer: no.
IBOutlets are needed to refer to elements of the UI.
If you don't need to access to such elements, you don't have to connect them with a IBOutlet
Answer is NO.
But here's a valuable tip, if someone sees this question/answer and goes gung ho and deletes all the unused IBOutlets.
If you remove an IBOutlet for a UIElement, make sure that the UIElement in the IB is not referencing to the now non-existant outlet. Otherwise, you'll have some weird crashes, that'll take quite some time to be resolved.
This is an issue that has had me in a soup many times. I am not sure if this has been fixed in the latest versions of Xcode, but its safer to check.
Im struggling to solve in a very clean way a problematic involving memory overload (management).
Im having a serie of view that include other views, in my project I have a situation like this:
MainView
|_PageView
|_CustomButton
soo far soo good, easy as a cake. CustomButton have a delegate (protocol) in it for some reasons, so we have in PageView a "for cycle" that creates N CustomButtons, set the delegate as self in PageView (PageVew extend CustomButtonDelegate) and release the buttons afer attaching them like
{
CustomButton *customButton_ = [[CustomButton alloc] initWithFrame:CGRectMake(100.0,50+(i*55.0),200.0);
customButton.delegate = self;
[self addSubView:customButton_];
[customButton_ release];
}
soo far soo good again. Button will be press, PageView get the protocol method, do some code and voilĂ , done. One problem is that at one point, MainView must remove PageView, so In a method I call
[pageView_ removeFromSuperview];
[pageView release], pageView_ = nil;
pageView_ = [PageView alloc] initWithFrame.....];
and I recreate the object with other data to display.
I noticed that PageView never gets release and removed from the memory because its retainCount is exactly how many CustomButton I created inside PageView and assign the delegate to self plus one of course. My question is, what is the cleanest way to remove safely all the objects and be able to remove PageView too, free the memory (because Im loading a quite large amount of data to display in it) ?
Right now i'm doing:
Create in PageView a NSMutableArray, that I CustomButton the objects in
it, and before to remove PageView, I cycle it and set the delegate = nil and then release
each object, after I release the NSMutableArray (called "holder").
But the problem is that if I want to add more objects of different types with other protocols, adding to this array, can lead to other problems of retaining the objects.
Where do I lack guys, knowledge so I need to study more (quite sure I can say) or do I need to approach with another OOD?
Thank you guys, im going overload with this problem and my brain is stuck in a close road. :)
Looks like your CustomButton's delegate is a retain property of CustomButton. Delegate should be an assign property, not retain nor copy. See here.
I have a nib file where I have a view that contains a background image, a button and another image that covers the full screen (a shadow) that needs to be moved to the front.
On the view, I'm creating child views, and after creating those and adding them using [self addView] I need to move to the front the shadow image.
I'm currently using the tag attribute to find that view, but I'm thinking there's probably a better way, by means of identifying the subviews I add in Interface Builder by some name.
I tries adding a IBOutlet to connect the subview with its parent, but it didn't work (and made no sense, since the subview is already connected to its parent in some way).
The IBOutlets way should work, and is probably the best way to do it. Make sure you made the proper connection in Interface Builder after you declared them in the .h file.
The iPhone does a lazy loading of view controllers. The nib might not have been loaded in initWithCoder or any init method for that matter as Kendall specified.
viewDidLoad is the preferred place to access anything from the nib if you want to access them before the view is displayed.
Hope that helps.
At what point are you trying to access the subviews? If you try within init of a ViewController, the IBOutlets will be nil. The first method you can get at them is probably viewDidLoad.
The reason it does make sense to do things this way is that IBOutlets are just direct pointers to some component, even if they are already subviews of something else. Just saves a lot of hunting.
Using the Tag is a perfectly valid way to locate specific views, so long as you're using the viewWithTag: method. If you're already using tags, there's no need to change to IBOutlets unless you just don't like calling viewWithTag:.