Is it inefficient to only override a method and only call super? - objective-c

Do I take a performance hit by leaving code like the following in my app?
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
I think the answer is yes, because it results in an unnecessary method call. But I wanted to make sure.

If you are asking about an extra method call, then the answer is yes, there would be a completely unnecessary call in the chain of method invocations leading to the call of the "real" viewDidUnload.
I would not go so far as to call removal of such methods an elimination of "performance hits": it is rather unlikely that you or your end-users would notice the effects of such micro-optimization.

Related

Method Swizzling - Please explain property mapping within this implementation

I was looking into an open source pull-to-refresh control and it swizzle lifecycle methods on a UIViewController category like so:
- (void)INBPullToRefreshView_viewWillAppear:(BOOL)animated
{
[self setClearNavigationBar:YES];
[self INBPullToRefreshView_viewWillAppear:animated];
UITableView *tableView = self.pullToRefresh.tableView;
tableView.contentOffset = tableView.contentOffset;
self.pullToRefresh.showPullToRefresh = YES;
}
I get that when viewWillAppear was called it mapped to the above method, and that calling [self INBPullToRefreshView_viewWillAppear:animated]; will map to the original viewWillAppear.
However, what does the following do?:
tableView.contentOffset = tableView.contentOffset;
Here's the github source for the control.
I would suspect the author is trying to use a side-effect of setContentOffset:, perhaps forcing a recalculation. But the author seems active on the project, so why not ask intmain in a github issue?
Of course the standard warnings that this kind of method swizzling is extremely dangerous and fragile apply.
I believe you're asking something unrelated to the swizzling itself?
Setting the contentOffset property will cause a scrollViewDidScroll: message sent to the delegate of your object. There's probably a cleaner way to accomplish that (or at least it should have a comment)

Deallocating window when animations may be occurring

My AppDelegate maintains a list of active window controllers to avoid ARC deallocating them too early. So I have a notification handler like this:
- (void) windowWillClose: (NSNotification*) notification {
[self performSelectorOnMainThread: #selector(removeWindowControllerInMainThread:)
withObject: windowController
waitUntilDone: NO];
}
- (void) removeWindowControllerInMainThread: (id) windowController {
[windowControllers removeObject: windowController];
}
I use the main thread because doing the handling on the notification thread risks deallocating the controller before it's ready.
Now, this works pretty well — except when there are animators currently running. I use animators in some places, through NSAnimationContext. I have looked at this QA, and the answer just isn't acceptable. Waiting for a while, just to get animation done, is really shoddy and not guaranteed to work; indeed it doesn't. I tried using performSelector:withObject:afterDelay, even with a larger delay than the current animation duration, and it still results in the animator running against nil objects.
What is the preferred way of doing controller cleanup like this? Not use NSAnimationContext but using NSAnimation instead, which has a stopAnimation method?
First, if some of your animations run indefinitely -- or for a very long time -- you're going to have to have a way to stop them.
But for things like implicit animations on views, you could simply use a completion method.
self.animating=YES;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
[[v animator] setAlphaValue: 1];
} completionHandler:^{
self.animating=NO;
}];
Now, you only need to poll whether your animation is running and, if it's not running, proceed to close your window.
One nice way to do the polling is to set a timer with a fixed delay. If the animation is still running, just reset the timer and wait another interval.
Alternatively, you could send a notificaton from the completion handler.
I haven't used NSAnimationContext (always did this with NSAnimation, but mostly for historical reasons). But the typical way I like to managed things similar to this is to create short-lived retain loops.
Mark's answer is exactly the right kind of idea, but the polling is not required. The fact that you reference self in the completion handler means that self cannot deallocate prior to the completion handler running. It doesn't actually matter whether you ever read animating. ARC has to keep you around until the completion block runs because the block made a reference to you.
Another similar technique is to attach yourself to the animation context using objc_setAssociatedObject. This will retain you until the completion block runs. In the completion block, remove self as an associated object, and then you'll be free to deallocate. The nice thing about that approach is that it doesn't require a bogus extra property like animating.
And of course the final, desperate measure that is occasionally appropriate is to create short-lived self-references. For instance:
- (void)setImmortal:(BOOL)imortal {
if (immortal) {
_immortalReference = self;
}
else {
_immortalReference = nil;
}
}
I'm not advocating this last option. But it's good to know that it exists, and more importantly to know why it works.

Why do we bother setting so many things to nil when a view is Unloaded?

-(void)viewDidUnload
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:LASTUPDATEDLOCATION object:nil];
[self setHeaderViewofWholeTable:nil];
[self setFooterViewofWholeTable:nil];
[self setHeaderActivityIndicator:nil];
[self setFooterActivityIndicator:nil];
[self setLastUpdated:nil];
[self setLblPullDowntoRefresh:nil];
[self setRefreshArrow:nil];
[self setContainerForFormerHeader:nil];
[self setFooterContainer:nil];
[super viewDidUnload];
}
I thought viewDidLoad is called the view itself goes nil. When we set the view to nil, wouldn't all those things automatically become nil?
What am I misunderstanding?
Before ARC you needed to manually release objects that you allocated. Setting a property that is marked retain to nil does the releasing. This is no longer necessary when you use the Automatic Reference Counting (ARC) feature, which is on by default in the compiler that comes with recent versions of Xcode.
Good news. As of iOS 6, viewDidUnload has been deprecated. In iOS 5 and earlier, when memory was low there was a chance that your view might have been unloaded (and to make sure there were no memory leaks, you released IBOutlets in this method). But this is no longer called in iOS 6, and thus, no longer a requirement.
Now if there is a issue with memory, your view controller can override:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Some of the other answers cover some of this but there is more to this. A view controller will have its viewDidLoad method called. Typically this results in IBOutlets being retained and possibly lots of other views and objects being allocated and retained. If all goes well, eventually the view controller is deallocated and all of those retained objects need to be released.
That's the simple, happy path. Under low memory conditions, in iOS 5 and earlier, it is possible that a view controller's view will be unloaded. The viewDidUnload method was a chance for you to clean up all of the other objects that were retained as part of the viewDidLoad process. And here's the main reason - at some point, viewDidLoad may be called again to redisplay the view controller's view.
Most people write their viewDidLoad method like it will only ever be called once. And this is OK if the viewDidUnload method properly clears up objects. If it doesn't, the next call to viewDidLoad will result in a bunch of memory leaks.
ARC pretty much eliminated the issue with the memory leaks if you didn't clean things up properly in viewDidUnload. But viewDidUnload was still helpful for cleaning up memory when needed.
As was mentioned, as of iOS 6, a view controller's view in never unloaded in low memory conditions and the viewDidUnload (and viewWillUnload) methods have been deprecated.
If your app still supports iOS 5 along with iOS 6, you still need to make proper use of viewDidUnload. But if you want to free up memory when needed, use didReceiveMemoryWarning.
We set so many things to nil to free up as much memory we can and reduce processor strain and increase battery life, not all objects automatically remove themselves from the queue.

KVO - How to check if an object is an observer?

When observing a value on an object using addObserver:forKeyPath:options:context:, eventually you'll want to call removeObserver:forKeyPath: on that object to clean up later. Before doing that though, is it possible to check if an object actually is observing that property?
I've tried to ensure in my code that an object is only having an observer removed when it needs to be, but there are some cases where it's possible that the observer may try to remove itself twice. I'm working to prevent this, but just in case, I've just been trying to figure out if there's a way to check first if my code actually is an observer of something.
[...] is it possible to check if an object actually is observing that
property?
No. When dealing with KVO you should always have the following model in mind:
When establishing an observation you are responsible for removing that exact observation. An observation is identified by its context—therefore, the context has to be unique. When receiving notifications (and, in Lion, when removing the observer) you should always test for the context, not the path.
The best practice for handling observed objects is, to remove and establish the observation in the setter of the observed object:
static int fooObservanceContext;
- (void)setFoo:(Foo *)foo
{
[_foo removeObserver:self forKeyPath:#"bar" context:&fooObservanceContext];
_foo = foo; // or whatever ownership handling is needed.
[foo addObserver:self forKeyPath:#"bar" options:0 context:&fooObservanceContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == &fooObservanceContext) {
// handle change
} else {
// not my observer callback
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc
{
self.foo = nil; // removes observer
}
When using KVO you have to make sure that both objects, observer and observee, are alive as long as the observation is in place.
When adding an observation you have to balance this with exactly one removal of the same observation. Don't assume, you're the only one using KVO. Framework classes might use KVO for their own purposes, so always check for the context in the callback.
One final issue I'd like to point out: The observed property has to be KVO compliant. You can't just observe anything.
Part of the NSKeyValueObserving protocol is this:
- (void *)observationInfo
which should list the observers.
EDIT
Useful for debugging only.
I underestand this an objective-c question. But since lots of people use Swift/objective-c together, I thought I point out the advantage of the Swift4 new API over older versions of KVO:
If you do addObserver multiple times for KVO, then for each change you’ll get the observeValue as many as the current number of times you’ve added yourself as the observer.
And to remove yourself you have to call removeObserver as many times as you added.
Removing it more than you’ve added will result in a crash
The Swift4 observe is far smarter and swiftier!
If you do it multiple times, it doesn’t care. It won’t give multiple callbacks for each change.
And only one invalidate of the token is enough.
invalidating it before beginning to observer or more times that that you’ve done observe will not result in a crash
So to specifically answer your question, if you use the new Swift4 KVO, you don't need to care about it. Just call invalidate and you're good. But if you're using the older API then refer to Nikolai's answer

Objective C Blocks: Is there a way to avoid 'self' being retained?

I'm trying to write this down as concisely as possible, but it's not easy to describe -- so thanks for reading =)
I'm the main developer of the Open Source iPhone Framework Sparrow. Sparrow is modeled after the Flash AS3 Library, and thus has an event system just like AS3. Currently, that system works by specifying selectors - but I would love to expand that system by allowing the use of blocks for event listeners. However, I'm stumbling over memory management issues.
I will show you a typical use-case of events - as they are handled now.
// init-method of a display object, inheriting from
// the base event dispatcher class
- (id)init
{
if (self = [super init])
{
// the method 'addEventListener...' is defined in the base class
[self addEventListener:#selector(onAddedToStage:)
atObject:self
forType:SP_EVENT_TYPE_ADDED_TO_STAGE];
}
return self;
}
// the corresponding event listener
- (void)onAddedToStage:(SPEvent *)event
{
[self startAnimations]; // call some method of self
}
That's quite straight-forward: When an object is added to the display list, it receives an event. Currently, the base class records the event listeners in an array of NSInvocation-objects. The NSInvocation is created in a way that it does not retain its target and arguments. (The user can make it do that, but in 99% of the cases, it's not necessary).
That these objects are not retained was a conscious choice: otherwise, the code above would cause a memory leek, even if the user removed the event listener in the dealloc-method! Here is why:
- (id)init
{
if (self = [super init])
{
// [self addEventListener: ...] would somehow cause:
[self retain]; // (A)
}
return self;
}
// the corresponding event listener
- (void)dealloc
{
// [self removeEventListener...] would cause:
[self release]; // (B)
[super dealloc];
}
On first sight, that seems fine: the retain in the init-method is paired by a release in the dealloc method. However, that does not work, since the dealloc method will never be called, because the retain count never reaches zero!
As I said, the 'addEventListener...'-method does, for exactly this reason, not retain anything in its default version. Because of the way events work (they are almost always dispatched by 'self' or child objects, which are retained anyway), that is not a problem.
However, and now we come to the central part of the question: I cannot do that with blocks. Look at the block-variant of event handling, as I would like it to have:
- (id)init
{
if (self = [super init])
{
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[self startAnimations];
}];
}
return self;
}
That looks great and would be very easy to use. However: when the user calls a method on 'self' or uses a member variable within the block -- which will be, well, almost always -- the block will automatically retain 'self', and the object will never be dealloc'ed.
Now, I know that any user could rectify this by making a __block reference to self, like this:
__block id blockSelf = self;
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[blockSelf startAnimations];
}];
But, honestly, I am sure almost all of the users would not know to do so or forget to do so. An API should be not only easy to use, but also hard to misuse, and this clearly violates that principle. Users of the API would most definitely misuse it.
What bugs me is that I know that 'self' does not have to be retained -- it works in my current implementation without retaining it. So I want to tell the block that he does not need to retain self -- me, the library, should tell the block that, so that the user does not have to think about it.
In my research, I have not found a way to do so. And I can't think of a way to change my architecture to fit that limitation of blocks.
Has anybody got an idea what I could do about it?
Even if you have not, thanks for reading this far -- I know it was a verbose question ;-)
I discussed this topic with Apple support, and they told me what I had expected: currently, there is no way that would let me tell the block that it should not retain self. However, they offered two ways to work around the problem.
The first would be to pass self as an argument to the block rather than a block variable. The API would become:
[self addEventListenerForType:ADDED_TO_STAGE
block:^(id selfReference, SPEvent *event)
The API would thus be responsible for passing in the (non-retained) self. Developers would still have to be told that they have to use this reference instead of self, but at least it would be easy to use.
The other solution that Apple has used in this sort of situation is to provide a separate method from release/dealloc for shutting down the listener. A good example of this is the "invalidate" method of NSTimer. It was created because of a memory cycle between NSRunLoop and NSTimer (by design).
I think the design is fundamentally flawed, blocks or no blocks. You are adding objects as event listeners before they are fully initialised. Similarly, the object could still receive events while it is being dealloced. The adding and removing of event listeners should be decoupled from allocation/deallocation IMO.
As for your immediate issue, you may not need to worry about it. You are having a problem because you are implicitly adding a reference to an object to itself (indirectly) by referencing it in the block. Bear in mind that anybody else supplying a block is unlikely to be referencing the object that generates the event or its instance variables because they won't be in scope where the block is defined. If they are (in a subclass, for instance), there's nothing you can do except document the issue.
One way to reduce the likelihood of a problem is to supply the object generating the event as a property on your SPEvent parameter and use that in the block instead of referencing self. You'll need to do that anyway because a block need not be created in a context where the object generating the event is in scope.