3
\$\begingroup\$

I have written an interface system for iOS that allows interface elements to be moved around on the screen as actors move around on stage. It can operate within a ViewController environment. It relies on messaging through the Notification Centre (obviously not LocalNotification).

The central object is the ARKView object. It has 'ARKState objects associated with it that contain information about where it should be (along with colour, alpha, transform, etc.) at each 'scene'.

My question:

I have Googled many moons to find a system which does this already. It turns out that having hundreds of objects on screen all sending each other notifications is not very efficient. Perhaps I can do it with delegates? I need a system in which I can precisely specify the position and properties of every element upon pressing a button or other stimulus.

Code

ARKView/receiveNotification:

- (void)receiveNotification:(NSNotification *)notification
{
    NSDictionary *dictionary = [notification userInfo];
    if ([notification.name isEqualToString:State] && [self.stateDictionary count]!=0) {
        NSString *stateId = [dictionary objectForKey:StateId];
        NSString *sender = [dictionary objectForKey:Sender];

        [self syncStateWithId:stateId andSender:sender];
    }
}

For each ARKView object, this method above attempts to find a state object that matches the name of the notification sent from another object, if it is found, it 'syncs' the state, giving the ARKView object the attributes it needs for that scene.

\$\endgroup\$
  • \$\begingroup\$ Hi. The code works, but I'm running up against some performance issues with what I want to do. I was wondering if anyone more experienced than me would have any ideas about how to do the same thing a better way. \$\endgroup\$ – arkaeologic Jul 15 '14 at 15:08
  • \$\begingroup\$ The link isn't particularly helpful given it required logging in to view the code. \$\endgroup\$ – nhgrif Jul 15 '14 at 18:40
6
\$\begingroup\$

There is one performance issue that can be addressed quite easily, and that is when we're even bothering to respond to the notification.

This if statement can be completely eliminated:

if ([notification.name isEqualToString:State] && [self.stateDictionary count]!=0)

The left half of the && can be eliminated by registering only for a specific notification. The right half of the && can be eliminated by registering/unregistering for the notification based on this property we're checking.

First, let's take care of registering/unregistering at appropriate times.

We only care about this notification if there's something in our stateDictionary, right? So, let's unregister when it's empty, register when it's not.

- (void)setStateDictionary:(NSDictionary *)stateDictionary {
    _stateDictionary = stateDictionary;
    if ([stateDictionary count] > 0) {
        // register for notification
    } else {
        // unregister for notification
    }
}

Now our code is already better because our object is only getting a message from the notification center if our object is in a state where it would care about the notification.

Now, as for registering only for this specific notification. That'd look something like this:

NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter addObserver:self
                selector:@selector(receiveNotification:)
                    name:State
                  object:nil];

We can make this better still if we have a reference to the specific object that might post the notification (if there's only one) and only listen to notifications of that type.


The important bit here is that now we're only listening to notifications that have the notification name we care about and we're only listening when our object is in a state that we would care about the notification. The if statement itself isn't a particularly expensive bit of code, however isEqualToString: isn't a particularly cheap method either, and there's no reason to run this (effectively twice, because NSNotificationCenter is already going to look at the notification names when determining who to send it to).

We can now refactor our method which responds to the notification into simply this:

- (void)receiveNotification:(NSNotification *)notification {
    NSDictionary *dictionary = notification.userInfo;
    NSString *stateId = dictionary[StateId];
    NSString *sender = dictionary[Sender];

    [self syncStateWithId:stateId andSender:sender];
}

And we'll get the same result.

So this code will run faster when it does receive the notification (because it skips the if check), and in general you'll be running faster overall because all the times that this method ran and the if check failed in the past, now the method will simply never be called and we'll never run through the if check.

\$\endgroup\$
  • \$\begingroup\$ That is very helpful. One problem that I am having is when I leave the app idle for two or three minutes, then perform some command such as a button press, there is some lag and the app jumps and starts. If I am pressing buttons quickly, this doesn't happen. Could this be because of how NSNotificationCenter works? \$\endgroup\$ – arkaeologic Jul 15 '14 at 22:21
  • \$\begingroup\$ It might be because of how you're using NSNotificationCenter, but it won't be anything internal to NSNotificationCenter. But it's really difficult to say, it could be any number of things. \$\endgroup\$ – nhgrif Jul 15 '14 at 22:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.