I cant seem to figure out how to set a scrollview's delegate as a viewcontroller within the scrollview.
ChildView *child = [[ChildView alloc] init];
_scrollview.delegate = child;
My child view controller is using the scrollview delegate:
#interface ChildView : UIViewController <UIScrollViewDelegate>
But it won't call scrollViewDidScroll;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"%f", scrollView.contentOffset.y);
}
Basically, I would like the scrollview offset of a scrollview to be passed to a view controller nested within it.
There are several ways you can assign a property in Objective C.
Assign means to simply performs a variable assignment and sets one variable equal to another. You won't be able to use the variable if it goes out of scope. This is typically only used for C primitives.
Weak tells the compiler to keep a reference to the object as long as someone else points to it strongly.
Strong tells the compiler to keep a strong reference to the given object. This means that the object won't go out of scope until the object holding it goes out of scope.
As #Aaron pointed out, delegates properties are set using assign. This means that if the variable you are setting the delegate to goes out of scope, the reference becomes invalid. To remedy this, you have to keep a strong pointer to the object around so that it does not go out of scope.
To do this, create a property in your main view controller:
#property (strong, nonatomic) ChildView * child;
Then, when you create the child view, set the property equal to it.
self.child = ChildView *child = [[ChildView alloc] init];
Now you can set the delegate and it won't go out of scope once your function terminates.
_scrollview.delegate = self.child;
child is getting deallocated by the time the delegate method fires.
From UIScrollView.h:
#property(nonatomic,assign) id<UIScrollViewDelegate> delegate; // default nil. weak reference
This is a weak reference, so it won't be retained. To fix it, create a strong #property in your main view controller, and set it to child.
Related
So I am writing a custom class that inherits from UIView. I have a bunch of subviews, that I add.
So run into 2 issues. IF I make the superview and subview references strong, the views are leaking. If I make them weak, they are not appearing at all. What am I doing wrong?
CustomUIView
#interface CustomUIView : UIView
#property(nonatomic, strong) AnotherCustomUIView *mySubView;
#end
#implementation CustomUIView {
- (void)initCommon {
self.mySubView = [self createSubView]
}
- (AnotherCustomUIView *) createSubView {
return [[AnotherCustomUIView alloc] init:self];
}
#end
AnotherCustomUIView
#interface AnotherCustomUIView : UIScrollView
#property (nonatomic, strong) CustomUIView *ownerView;
#end
#implementation AnotherCustomUIView
- (id)init:(CustomUIView *) ownerView {
self = [super init];
if (self) {
self.ownerView = ownerView;
self.delegate = self;
}
return self;
}
#end
Based on your code, I think you have a confusion between strong and weak references, and how those relate to memory management.
First, here is a great description of strong vs. weak: https://stackoverflow.com/a/11013715/700471
In your specific case, the ownerView property on AnotherCustomUIView should be weak as it is pointing to a senior object. Delegate references are also weak, I don't know that setting self.delegate = self; has any negative impact.
Edit:
To clarify, adding a view as a subview of another view creates a strong reference to it. Further strong references should not be necessary once your view is part of the view hierarchy. So you have this code:
[mySeniorView addSubView:myJuniorView]; // myJuniorView will not be released.
mySeniorView.juniorView = myJuniorView; // This shouldn't be a strong reference.
In the above code, if mySeniorView.juniorView is a strong reference, it's redundant. If you remove myJuniorView from the view hierarchy it won't be deallocated because you still have another strong reference to it. If .juniorView is a weak reference, then removing myJuniorView from the view hierarchy will cause it to deallocate and set .juniorView to nil.
That's why, for example, all of your IBOutlet properties should be weak; because the things in your xib that you're connecting them to are part of the view hierarchy and therefore will not dealloc because their senior views have strong references to them.
So, while I pointed out that your junior object should not have a strong reference to your senior object (neither one will deallocate; this is called a retain cycle and results in memory leaks) probably your senior object shouldn't have a strong reference to your junior one, unless you want it to hang around even when it's not in the view hierarchy. These are views, after all; I would say create your junior object, pop it into the view hierarchy and then save a reference to it in a weak property so you can access it for as long as it exists. Then, when you remove it from the view hierarchy, your reference nils out.
Anyway, hope that helps.
Reference: Apple Docs, addSubview:
Almost always the reference to the parent should be weak. This is typical chain of command. The parent creates and "owns" the child, and the child merely observes its parent. You want the child to be existentially dependent upon the parent, not the other way around.
iOS newbie here.
I have a class that is a subclass of UIViewController called CommonVC. It does not have a scene directly associated with it. It declares a property in the .h file:
#property (strong, nonatomic) SomeClass *someClass;
I modify one of my scene View Controllers to have CommonVC as the super class instead of UIViewContoller:
#interface SceneViewController : CommonVC
In SceneViewController, I create a lazy instantiation getter for the property:
- (SomeClass *)someClass
{
return self.someClass ? self.someClass :
(self.someClass = [[SomeClass alloc] initWithData:#"xxx"]);
}
This compiles OK, but crashes at runtime with EXC_BAD_ACCESS(code=2,...). The debugger is pointing at the override method and 'self' is nill (0x00000000).
The first reference to the getter (self.someClass) is in the viewDidLoad method. I would think that the view controller and its super class would have been instantiated at this point.
What is going on?
Thanks.
return self.someClass ? self.someClass
This is an endless loop. the code self.someClass is equivalent to [self someClass]; so that will get called endlessly. You will want to change your self.someClass to _someClass.
First of all, if you call self.someClass in your getter you end up in a loop as self.someClass it's actually calling that same method. This is probably the reason of your crash.
Secondly, every UIViewController instance always has a view (the scene you're talking about?) associated with it, hence all the -viewDidLoad, -viewWillAppear, etc. methods.
Lastly, it's very bad to have a 'common view controller' to handle stuff. You may want to use the delegation pattern or the singleton pattern instead.
My problem is that I want to give a object back from child view (controller) to a parent view (controller). I pass the object with a call like this:
parentView.someObject=objectFromChild;
Everything is okay until the child view gets deleted (because it is poped up and no pointer shows on it) but then the object passed from child view to parent view gets also deleted. Can anyone tell me how to make it possible to save my object(even if the view which created it, is deleted)? With NSString my method works very well...but with objects I always get EXC_BAD_ACCESS
Make sure the parent object is retaining it.
Sounds like there are a couple of challenges here and I'll try to give some simple tips.
Passing Data
If you want to pass an object upward from a child to a parent, design your Child class so that the object or variable is a public property. Then, any other object (like Parent objects that own the Child) can access that property.
Keeping Data Alive
Usually EXC_BAD_ACCESS means the object has already been deleted by the system. Tell the system you want to hang on to the object by setting 'strong' in the property declaration, and this will take care of your EXC_BAD_ACCESS problem.
Take a look at the following code for an example of how to implement a very simple parent/child data relationship and retain data.
//****** Child.h
#interface Child : NSObject
// Child has a public property
// the 'strong' type qualifier will ensure it gets retained always
// It's public by default if you declare it in .h like so:
#property (strong, nonatomic) NSString *childString;
#end
//****** ParentViewController.h
#import <UIKit/UIKit.h>
#import "Child.h"
#interface ParentViewController : UIViewController
#property (strong, nonatomic) Child *myChild;
#end
//****** ParentViewController.m
#implementation ParentViewController
#synthesize myChild;
- (void)viewDidLoad {
[super viewDidLoad];
// create child object
self.myChild = [[Child alloc] init];
// set child object (from parent in this example)
// You might do your owh setting in Child's init method
self.myChild.childString = #"Hello";
// you can access your childs public property
NSLog(#"Child string = %#", self.myChild.childString;
}
I'm writing iOS apps using ARC and targeting iOS 5+.
Suppose I write a custom view object that has a delegate property. In declaring the delegate property, I make it a weak reference to avoid a retain cycle, so that when the actual delegate object (the controller) is destroyed, my custom view will also be destroyed, as follows:
#interface MyCustomView : UIView
#property (nonatomic, weak) id<MyCustomViewDelegate> delegate;
#end
All is good.
Ok, so now I'm writing the controller object, and it has references to two view objects: my custom view and an Apple-supplied UIKit view, both of which declare delegate properties, and the controller is the delegate for both views. Maybe it looks something like this:
#interface MyViewController : UIViewController <MyCustomViewDelegate, UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) MyCustomView *customView;
#property (nonatomic, strong) UITableView *tableView;
#end
#implementation MyViewController
- (void)viewDidLoad
{
self.customView.delegate = self;
self.tableView.dataSource = self;
self.tableView.delegate = self;
}
#end
My question is this: Do I need to override dealloc to set either or both delegates to nil?
I mean, as I understand it, the delegate property of the UIKit view (in this case, tableView) isn't actually declared to be a weak reference, but rather an __unsafe_unretained reference, for backwards compatibility with non-ARC version of iOS. So maybe I need to write
- (void)dealloc
{
_tableView.dataSource = nil;
_tableView.delegate = nil;
}
Now, if I do have to override dealloc, I still don't have to set _customView.delegate = nil, right? Because that was declared (by me) to be a weak reference, so it should be set to nil automatically upon the destruction of MyViewController.
But on the other hand, I'm not targeting non-ARC versions of iOS, nor do I intend to. So maybe I don't need to override dealloc at all?
Setting non-weak delegates to nil is generally a good idea unless you know you don't have to. For UITableView and UIScrollView, I've experienced crashes on previous iOS versions with the following steps (it may help to run with zombies enabled):
Scroll really fast.
Press Done or the back button or whatever to dismiss the VC.
This appears to happen because the scrolling animation is retaining a reference to the view, so the view outlives the VC. It crashes when sending the scroll event.
I've also seen crashes after dismissing a VC containing a UIWebView while a request is being loaded, where simply setting the delegate to nil was not sufficient (I think the workaround was to call [webView loadRequest:nil]).
If the only strong reference to said tableView is your sole MyViewController controller, you don't need to manually set UITableViewDelegate or UITableViewDataSource to nil.
The reason is that once the dealloc method on your MyViewController is called, the tableview will also be destroyed along with the controller (that is, once again, as long as the only reference to it is your sole controller MyViewController class).
If you have other strong references to this tableview, such as other controllers, it would then be possible that the tableview could then exist longer than the MyViewController class. In such a case, it would be necessary to set the UITableViewDelegate and UITableViewDataSource to nil in the dealloc method of MyViewController because, as you mentioned, these properties are NOT weak references and will not automatically be set to nil.
However, this sort of situation is pretty rare in my experience though.
Most of the time, I don't worry about setting these to nil honestly, but it is a defensive programming practice.
See this post also:
In dealloc method set any delegate to nil is needed or not needed
The only reason you would want to explicitly set the delegate and dataSource to nil is if the customView or the tableView could out live the view controller. Setting them to nil would guard against the delegate or dataSource referencing a deallocated object.
If the customView and tableView will be deallocated along with the view controller, there is no need to nil out the delegate and dataSource.
I have added my custom UICollectionViewController as an object (A) to the interface builder, and given it the proper custom class.
this EOCollectionViewController is also an IBOutlet in my main Viewcontroller
I have added the UICollectionView to the main view of the application.
I have made all the links possible. UICollectionView has the object (A) as a delegate and a datasource.
The object (A) has the UICollectionView linked to the view property. (not the self.collectionView property, as this is not there)
It all works well. Collection view get's filled. The CollectionViewController resides in the property of my main view controller.
Only the self.collectionView is nil...when I try to reach it from within the UICollectionViewController.
My first guess is that "extra" UIControllers used in IB do not get initalized in the same way. I also tested, and viewDidLoad and init never get called for these extra objects.
Attached the header of EONoteController (and the IBOutlet added as a work-around)
#interface EONoteController : UICollectionViewController<UIGestureRecognizerDelegate>{
__weak IBOutlet UICollectionView *cvNotes;
}
#property (nonatomic) EDAMNotebook* notebook;
#property id <EODragHandler> draghandler;
#end
My first guess was to "fix" it, in the viewDidLoad, as follows
self.collectionView = self.view, but viewDidLoad never get's called.
What am i Missing?