i want to write my own photogallery like the original "Photos.app" from apple.
I´ve created a UITabbarcontroller in the AppDelegate and then an "ImageViewController" and a "VideoViewController".
In the "ImageViewController" i´ve added an UIScrollView and then made an instance of my own "PhotoGallery" with different properties like imagePerRow, images, paddings etc.
For the "PhotoGallery" i´ve created a new objective-c class as a subclass of "NSObject", where i´m positioning all the different images as UIButtons.
Then i´ve added another function which describes the arrangement for all the images when the device orientation has changed. And the dealloc-function. Thats all.
This class works great, also the rearrangement when the device orientation has changed. The problem is, if i simulate a memory warning in the ios-simulator, the first time the PhotoGallery gets correctly dealloc but if i simulate a warning again, i get a error-message: "[PhotoGallery release]: message sent to deallocated instance ".
I thought its because of the subclass as NSObject, right?
Then i´ve tested it as a UIView. With the same error. So know i don´t know what to do anymore. Hope you understand what´s the problem and you would give me some hints on that..
Think about calling the init-function again? How? Need "drawRect"? I´ve no idea.
Thanks for your time and help,
G.
You're probably not setting the property which holds a reference to the PhotoGallery to nil.
ie. You're keeping a reference to a deallocated instance, and attempting to call release on it.
bad example:
- (void) didReceiveMemoryWarning
{
[photoGallery release];
}
safe(r) example:
- (void) didReceiveMemoryWarning
{
[photoGallery release];
photoGallery = nil;
// or combine both actions if your property attributes are set up to accommodate it:
// self.photoGallery = nil;
}
In the bad example, photoGallery still holds a reference to a now-deallocated instance, and the second memory warning will attempt to send a message to it.
In the safe(r) example, photoGallery is nil, and sending a message to nil is safe.
Related
I'm trying to change the title of a button after I call back from a notification but it doesn't respond at all. I checked it's not nil and checked the text Im' assigning and all is good. I made the property type strong instead of weak but no success.
- (void) setButtonTitleFromSelectedSearchResult:(NSNotification *)notif
{
[self popController];
self.sourceMapItem = [[notif userInfo] valueForKey:#"SelectedResult"];
NSLog(#"The Selected Result is: %#", self.sourceMapItem.name);
//Testing
NSLog(#"%#", self.fromButton); // check it's not nil
[self.fromButton setTitle:self.sourceMapItem.name];
}
With WatchKit, if a user interface element isn't currently visible, it cannot be updated. So, if you've presented another interface controller "on top", you can't update any of the presenting controller's interface elements until you've dismissed the presented controller. At that point, you can safely update the presenting controller in its willActivate method.
SushiGrass' method of passing blocks is certainly one valid approach. In my testing, however, I ended up having to manage multiple blocks, and many of the subsequent blocks reversed what earlier queued blocks had accomplished (for example, first changing a label's text to "foo", then "bar", then "foo" again. While this can work, it isn't optimal.
I'd suggest that anyone who is working on a WatchKit app takes a moment to consider how they want to account for off-screen (i.e. not-currently-visible) interface elements. willActivate is your friend, and coming up with a way to manage updates in that method is worthwhile if you're moving from controller to controller.
For what it's worth, I've encapsulated a lot of this logic in a JBInterfaceController subclass that handles a lot of this for you. By using this as a base class for your own interface controller, you can simply update your elements in the added didUpdateInterface method. Unfortunately, I haven't yet had the time to write proper documentation, but the header files and sample project should get you going: https://github.com/mikeswanson/JBInterfaceController
I'm using latest XCode 6.3 and below code working with me.
self.testBtn is bind with Storyboard and its WKInterfaceButton
I also have attached screenshot with affected result.
I'm setting initial text in - (void)willActivate
- (void)willActivate {
[super willActivate];
[self.testBtn setTitle:#"Test"];
[self performSelector:#selector(justDelayed) withObject:nil afterDelay:5.0]
}
-(void)justDelayed
{
[self.testBtn setTitle:#"Testing completed...!!"];
}
If you're using an IBOutlet for the property fromButton be sure that is connected to WKInteface on the storyboard, like below:
I solved this kind of issue by creating a model object that has a property that is a block of type () -> (Void) (in swift). I create the model object, set the action in the block that I'd like the pushing WKInterfaceController to do on completion, and finally pass that model object in the context to the pushed WKInterfaceController. The pushed WKInterfaceController holds a reference to the model object as a property and calls it's completion block when it's done with whatever it needs to do and after func popController().
This worked for me for patterns like what you are describing along with removing rows on detail controller deletion, network calls, location fetches and other tasks.
You can see what I'm talking about here: https://gist.github.com/jacobvanorder/9bf5ada8a7ce93317170
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
I have a view, which is subclass of UIWebView. It has a property called Contact which is a managed object. The view uses templating engine to create a html with the object and then load into UIWebView. I thought it would be a better idea to monitor the object in the view itself, such that whenever something changes in the object, the view refreshes automatically. So, observed for certain attributes of the managed object in the view itself. And then to avoid the notification coalesce, I have made it such that the reload is done with
[self performSelector:#selector(refresh) afterDelay:0 ].
It refresh the webview automatically whenever it finds the change but also gives some strange crash. The crash says [MyWebView retain] message sent to deallocated object. I know I have properly removed observing values in dealloc method. But, it seems like dealloc gets triggered after a while. I have a strange issue related to releasing the view. The view stays for a while, although the view controller is already released and then releases after may 2/3 seconds. It is really strange. I think the crash is because of this.
Please do suggest me any idea. I will be glad to hear your suggestion. There are something wrong certainly, if anybody could point me I would really be grateful.
Using the delegate design pattern can cause EXC_BAD_ACESS KERN_INVALID_ADDRESS crashes if not used properly. If you have processing that is running in background threads that use the delegate design pattern, where in the object you set SELF as the delegate then you must remove SELF as the delegate in the dealloc method (even under ARC) by setting the delegate reference to nil, or there is a possibility that the object will try to call back into your deallocated object using the delegate design pattern. So if you have something like this in your object.
[_xmlParser setDelegate:self];
you should always have a dealloc method even under ARC to prevent the possibility of a crash in the case where your object gets destroyed while still doing work. It is very common to have your object destroyed while doing work. imagine a UIViewController that shows images from the internet. If you had a FetchImage class that used the delegate design pattern to lookup images that then calls a routine on the object when the lookup finishes, it is easily for the user to pop into and out of your UIViewController while your FetchImage object is still doing work on the background thread. You might not ever notice this when testing, but if you have hundreds of users, some of them will notice because the app will crash when your object tries to call a method on the SELF reference.
If your object uses the delegate design pattern, always have this to cleanup:
#pragma mark - dealloc - cleanup delegate references to prevent callbacks into deallocated objects (EXC_BAD_ACCESS / KERN_INVALID_ADDRESS)
- (void)dealloc
{
[_xmlParser setDelegate:nil];
// for non ARC based code you would also call: [super dealloc];
}
search every class in your project, if you have setDelegate:self or delegate = self then your users are most likely experiencing race condition crashes with your app if you don't have a dealloc cleanup method as described above. If you don't have the dealloc, add it even if you never see crashes when testing. -rrh
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 am converting an iPhone app to a Universal app. I have a NIB view which I want to use on the iPad as is but resized and positioned. On the iPhone I am initializing normally with initWithNibName...
EventEditViewController *eventEditViewController = [[EventEditViewController alloc] initWithNibName:#"EventEditViewController" bundle:nil];
I found that this did not work well for me on the iPad for various reasons. So I created my own initialization method to call instead when running on the iPad...
EventEditViewController *eventEditViewControllerForIPad = [[EventEditViewController alloc] initWithFrame:iPadFrame eventDate:longDate event:eventName delegate:self];
This solved a couple of problems. One how to resize and position the view where I wanted it and how to properly initialize certain variables. I am actually passing more variables than you see here.
It works really well, but I just now noticed that, unlike initWithNibName viewDidLoad fires before my initWithFrame method. I only found this out because a variable I was trying to access in viewDidLoad was showing up as a zombie and I thought I was initializing it in my initWithFrame method.
I was surprised by this behavior. Is it normal? It doesn't make sense to me that the view would be loaded before the named initMethod in the alloc/init call.
I am now wondering if what I am doing might not be a good thing. Like I said it works really well, but should I not use my own initialization method here?
If it's ok to do it this way, maybe someone can explain why the view loads before the init method.
Thanks,
John
If your -initWithFrame:... method is accessing the view controller's view property, -viewDidLoad will be called before the init method completes because the view accessor will cause the view to be loaded.
As for whether it's okay to use your own method, it should be fine provided that your init method calls the designated initializer for the class.
Initializing member variables should be done in viewDidLoad or awakeFromNib.
awakeFromNib is the first method that gets called when a view comes to life from a Xib.
It's preferred to use viewDidLoad for allocating memory for huge arrays since you can deallocate them in viewDidUnload.
Both navigation controller and tab bar controller uses view loading methods to unload views when other views demand more memory.
Allocating in anyother methods should be avoided as far as possible.
From Apple's PageControl source code
// load the view nib and initialize the pageNumber ivar
- (id)initWithPageNumber:(int)page
{
if (self = [super initWithNibName:#"MyView" bundle:nil])
{
pageNumber = page;
}
return self;
}
You can have your own custom init method defined in that EventEditViewController and you can use a custom method like above to initialize your viewController and set as many member variables as you want like iPadFrame, longDate, eventName etc in your case.
Just make sure you call it exactly as above as it's important to call super implementation in such custom init methods.
Also just to shed more light on where you should release arrays you created in viewDidLoad method, it's the dealloc method first in addition to viewDidUnload. The reason behind this is viewDidUnload method doesn't always get called. It gets called only when application starts receiving memory warnings. As compared to this, the method dealloc gets automatically called always when you release the initialized viewController and it's retain count reaches 0. You should release the arrays you initialized viewDidLoad method and your other retain properties in dealloc method.
Also keep in mind that when the app receives memory warning, it's actually a chance to free up additional memory. Also the viewDidUnload method gets called for all the viewControllers in memory except visible one at that time.