Tuesday, April 7, 2009

One of Those Days


Rather than pushing towards getting new features out, I decided to take some time to go back and fix some bugs.

I do my work on two computers; an iMac and an iBook. Most of my time I'm doing my work and testing on my iBook. Most of my testing recently has been done with one character loaded. Earlier today I decided to do it on my iMac instead, for which I have two characters saved in my preference file. When I launched the app on it, I realized how much more unstable it ran. NSNotifications were being called twice (no idea why) and a character's skill in training was being showed on another character's tab.

That's the start of my problems today. I'm pretty sure I'll run into more.

Monday, April 6, 2009

Project Update

I've made quite a few changes with my last submission I did just a few minutes ago as of this writing. I'll try my best to list them.

Noticeable Changes:
  • Smaller application size. Things were way too big and spaced out so I compacted much of the information.
  • Learned skills outline view now has learned skills shifted farther to the left. Ended up doing something Apple probably doesn't recommend but oh well.
  • Current skill in training information now showing. Untested in circumstances such as what happens when a skill finishes training.
  • Dynamically estimates the number of skill points your character currently has.

Under the hood changes:
  • Reduction in the number of active NSTimers on the run loop. Got everything related to the current skill in training reduced down to only using one timer per update.
  • API controllers now post notifications whenever they finish updating the Core Data store rather than going through a series of delegate callbacks. 
  • Tighter linkage between the character data and tab controller for passing data between the two.
  • Server status bindings properly display values without manually binding every time an update occurs.
  • A few memory leak fixes.
Hopefully this week I can announce a beta. For now, it's almost 5 am and I still haven't slept. Time to crash.

Sunday, April 5, 2009

The Skill Point Timer Problem


Note: This post is more or less a discovery of common sense solutions. When I realized the best, and conveniently the least complicated, solution it was only then I saw how far off the deep end my mind went.

The frequency of skill points updating can easily be calculated and the value can accurately be guessed. A running counter of the current skill points for an app of this nature would almost be a necessity.

Here's the simple reason why there is a problem: NSTimers just aren't reliable. From Apple's own class reference of NSTimers: "If the firing time is delayed so far that it passes one or more of the scheduled firing times, the timer is fired only once for that time period; the timer is then rescheduled, after firing, for the next scheduled firing time in the future."

So say the counter is running on the main thread and is updating the current NSManagedObject being learned. If you press a menu item and hold it long enough, it could easily cause your skill points counter to be thrown off.

One might then suggest why not do the updating on a separate thread? That would certainly solve the issue of not having user or other events interfere. But now you're creating a whole slew of new problems. First off, I have to have a perpetual secondary thread for the shear purpose of keeping a counter. This means doing quite a bit of learning on maintaining an active run loop. Then there's the problems of sending messages to the new thread. Ok, so even if I somehow manage to solve all those problems, there's still remains another unsolved one: Context locking. Currently, I have two contexts; one for displaying data and one for updating. That was tricky enough to figure out. If, for whatever reason, either of them get locked long enough, I'm back to the same problem. Adding a third one just for the sake of updating one object on a timer is just plain silly.

Then I thought of a 3rd idea. Why not a 3rd thread! The main one for user interaction; A second one for updating values; Then the third to have a timer that only updates a variable. The second one would query the third one for the proper value. If, for whatever reason, the second thread pauses too long, the counter will still continue. Brilliant!

And then it dawned on me. Why am I using an NSTimer for keeping the accuracy of the skill point values? And why does something so simple have to have multiple threads? The real solution is just create a static date to reference for computing the amount of skill points. Have a timer be set up on the main thread to call an update method. This method takes the date, computes the skill points the character would have based on that date and updates the object. If somehow the timer doesn't fire one or more times consecutively, the next time it does it will update to the appropriate values.

Saturday, April 4, 2009

Cocoa Bindings and NSWindowController

Having a pretty eventful weekend so I haven't had much of a chance to update.

While figuring out the kinks to removing characters, of which I think I've got all figured out, I ran into an interesting problem. My character tabs are handled with an NSWindowController tied to a nib. This nib's file owner object (CharacterTabConroller object) contains outlets, one of which is tied to an NSView object (rather than an actual NSWindow). In this nib there is also an NSArrayController.

Here's where it gets interesting. Apparently, in some way, this NSArrayController adds a retain count to the NSManagedObjects (in my case it was the character's learned skills) it's controlling. Originally, I set it up so that the character's data gets deleted first. However, after getting a series of bad access crashes, I noticed in the debugger there was a call to NSAutounbinder. Ok, so maybe I should be releasing the NSWindowConroller first and then removing the actual character data? Nope. Still crashing.

After a bit of digging on the internet, apparently you actually have to unbind the NSArrayController before releasing its enclosing NSWindowController. Personally, I think this is counter intuitive.

Friday, April 3, 2009

On a Personal Note


Completely unrelated to my development but I figured this would be a good place to start looking.

Currently, I don't play much because of developing this app. Hopefully that'll change after a month. In the mean time, though, I am looking for a decent sized, established corp. A corp that actively helps each other with missions. Preferably a corp that has stations in 0.0 sec.

Where I am now there's only about 5 or so members. While I think they're a great bunch, chances are when I log on they probably won't be on. Part of what makes an MMO fun is the people.

So yeah, I'm looking to move on up. I only have a few month's worth of skill points accrued and not too much in assets. If I was to join another corp, though, I wouldn't be looking for handouts. So, if your corp fits my description of what I'm looking for and its willing to take me on, post a reply on how I could get in touch with you.

Thursday, April 2, 2009

Tracking Skill Points and Removing Characters

As per title, these two things are part of my work in progress. Normally, I'd hold off on getting new features in but my weekly write up on my progress does look better when it appears that I've been tackling more problems.

For skills currently training, I first calculated how many seconds it takes to get 1 skill point based on the character attributes and skill being trained and then set up a timer to fire at that interval to update the value. The learned skill managed object's skill points learned value gets updated also at that frequency and I set the context to be updated once every 20 seconds. I'm not sure I'll stick with that value but I think it's a pretty reasonable number. Also, while, in the process of typing this, I don't recall writing in code to stop the timers once the accrued skill points hit the requirement for the next level. Hmm...

Removing characters is a work in progress now. For a while what bugged me was the retain count on some of these objects, some being as high up as 5. I went in and wherever I could, I put in releases. I then realized my setDelegate: methods were also affecting the retain count so I fixed those. Finally, after a while of probing, I came to find out NSTimers were also adding to the retain count, forcing make methods to kill the timers before sending a release message to the object. I'm now at the point where I'm passing a release message to an NSXMLDocument but its causing me to crash every time I access that object. It's late, though, so it'll be tomorrow's project.

As always, delete the contents of ~/Library/Application Support/EVE Mac Suite/ before running this app. I may later one, for my own sake, too, create a way to allow the user to completely wipe the cache and force the app to rebuild its database.

On a final note, Sam Adam's Cherry Wheat is damn fine beer.

Wednesday, April 1, 2009

NSNotifications

I've known about NSNotifications and what they can do but I apparently was completely oblivious to them when writing a lot of my code. In writing a response to Erik on my previous post I realized that I've been making my life harder with some of my programming. For example, before you can view what a character has learned, the character sheet xml needs to be processed. Before that can happen, though, the skill tree xml file needs to be processed. The way my code works is via a series of call stacks. While debugging I've noticed the call stacks get as high as 6 stacks and maybe even more due to a series of delegate callbacks.

While the application may still run fine with that, there is one big problem: it's blocking the run loop.

With a large call stack, the application could be creating a large amount of autorealeased objects that gobble up memory and won't get released until the application's main thread hits the run loop. I don't foresee such objects being around that long but what if you're in the middle of intense EVE PvP and you suddenly get a deadly lag spike because this app was running in the background and suddenly required a lot of memory? You wouldn't have guess it was this app but that wouldn't change the fact that this app made your EVE experience worse.

The more noticeable problem, though, is that huge call stacks that last several seconds prevent events from being received on the run loop. The application essentially hangs until it finishes its task.

Sometime this weekend, hopefully, I'll be making a pass through some of the code and changing it around to use NSNotifications. I might also post up some diagrams about the logic flow of my code. Its getting complicated to the point where I'm occasionally struggling to keep it all straight in my head.

Oh, and as for the NSManagedObjectContextDidSaveNotification thing you mentioned, Erik, I did some research on it. NSNotifications on a spawned thread never hit the main thread. NSDistributedNotifications are needed for that and I'm not sure I want to go there. A general did save notification might be too broad of a notification, too. For example, updating a character's learned skills table view is done via a notification. If only character A's data changed, there isn't any need to update character B's table view. What I might end up doing is making my set of notifications based on what kind of data gets updated.