Main

March 17, 2013

OpenCV on iOS working again

I just updated the OpenCV on iOS Xcode project to use the git repo as a submodule for OpenCV.  I've also fixed the SURF demo.  Take a look!

(The git mirror that I had created for OpenCV's svn is now gone.)

Note: If you've cloned a previous version, use git submodule init and git submodule update after pulling the latest rev to pull the correct version of OpenCV. If you get build errors, you probably need to do this.

May 02, 2011

Simplified building of OpenCV on iOS is here

Check this out. Instead of several steps, some ugly CMake voodoo, and some work in the shell, now all you have to do is pull down a git repo to make OpenCV work on iOS. A couple of people have tried it out and found it functional. Let me know at "my first name at this domain dot calm" whether it works for you. If anyone would like to use the example project to make instructions on how to make it work in a new project or even generate some project templates, get in touch or give it a go and submit a pull request on github.

November 16, 2010

#protip: In app purchase requires ability to sell apps

If you're new to the iPhone scene or working on In App Purchase code using a client's iTunes Connect setup, be aware that you must have all of your banking and financial information set up such that you *could* sell something on the app store, even if your app is not yet released, in order to test In App Purchase.

I have a client who hasn't yet released any applications. (SOON! Right, Randy?) We ran into this problem together and spent a couple of hours pulling our hair out over this one. I suggested that he go ahead and add his banking information before escalating the problem to our friends at Apple and everything started working one he got himself setup for payments. Unless I'm missing something obvious in the docs, and since there is no error message that comes back from the Store Kit callback indicating why an item is invalid, there is really no way that one would know that other than to have stumbled upon it like we did.

(facepalm)

October 04, 2010

Mercurial as überVCS

I remember a Q&A session during the most recent 360|idev where several of us took to discussing our favorite flavors of version control. During that discussion I asked whether anyone knew of a sort of über version control system that would allow me to pull code from each of the three most popular version control systems (git, mercurial, subversion). Given that there is a lot of fantastic code on bitbucket (mercurial), github (git), google code (subversion / mercurial), and elsewhere, we all agreed that it would likely prove very useful to be able to use some overarching version control system to pull up-to-date code from more or less anywhere in a manner similar to the way svn-externals handles pulling subversion-based subprojects into a subversion working copy.

I have a number of projects using Subversion for version control, a few using Mercurial, and I even maintain a private branch of Three20 using git. When an external project is using the same version control system as the project itself, life is generally simple. In the gogoDocs subversion repository, for example, the svn-externals mechanism happily keeps the GData API at a specific revision for me. Almost all of my projects use code from some other project that uses a "foreign" version control system. In gogoDocs, where I keep my copy of the GData API close to the bleeding edge, the version of Three20 I'm using is woefully out of date. The reason for this is pretty simple. It's difficult to update Three20 because it requires a lot of voodoo to pull the changes from the git repository. In order to keep Three20 up to date, I would have to keep the .git directory with the Three20 code inside of my working copy, making sure to svn-ignore that directory and never check it in to subversion (I did that once accidentally...not pretty). If I were to try to update my copy of Three20 with the canonical version on github, I would also have to remember to commit those changes into my own subversion repository. This always causes some problems on whichever of my other development machines is behind the bleeding edge. In order to update to the latest Three20 on another development machine, I have to remember to git pull before I svn up or things start to get scary; this means that I have to keep the .git directory in all of my working copies up to date. What a pain. Like many programmers, I'm almost inexcusably lazy, so I don't bother. I need an überVCS.

Here's what I think such an überVCS should do:
  • It should be distributed, allowing me to branch and commit locally. I have lately taken to making atomic local commits and using local branches to experiment and I really miss that workflow when I'm working in svn.
  • It should be ubiquitous and familiar. I don't want to learn another version control system, okay? I already have three version control systems competing for the mental space that used to hold one. I really just want one version control system that deals with everything.
  • It should be able to easily pull subprojects from git, mercurial, and subversion repositories in a manner similar to subversion's svn-externals.
  • It should allow me to work within those subprojects through plugins or natively so as to avoid the use of "foreign" version control command sets. In other words, I should be able to use the überVCS's command set inside of, for example, a subversion subproject. Ideally, I could work in one version control system and have subprojects from other version control systems just work somehow.


Using a few plugins, Mercurial can act as The überVCS I've been looking for. It fulfills the first two requirements out of the box. It's distributed and familiar. As far as I'm concerned, it acts a lot like a distributed version of Subversion, which is the old(ish) school version control system I'm most familiar with, whereas git feels like it is from someplace like Mars. Your mileage may vary.

The third requirement "to easily pull subprojects from git, mercurial, and subversion repositories" can be fulfilled by installing my fork of hgexternals from bitbucket. It's just a simple merge of the fork from adri (available here) with the latest code from the original author of hgexternals available (here). Once my little fork of hgexternals is installed, you can place an .hgexternals file in the root of your main project and Mercurial will pull code from pretty much anywhere when you run the hg externals command. If you wanted to pull the gdata objective-c client (google code / Subversion), the hgsubversion project (bit bucket / Mercurial), and mogenerator (github / git) into a contrib subdirectory of your project, you would put the following three lines into your .hgexternals file.
./contrib/gdata http://gdata-objectivec-client.googlecode.com/svn/trunk/ svn
./contrib/hgsubversion https://jonmarimba@bitbucket.org/durin42/hgsubversion hg
./contrib/mogenerator http://github.com/rentzsch/mogenerator.git git
If you then run hg externals in the project. You'll see that the subversion, mercurial, and git repositories are checked-out much like subversion handles svn-externals. Note: Be aware that the hgexternals python scrip will actually run svn or git binaries in a subprocess, so svn and git will have to be installed for this to work properly. If you wanted to hold the external project to a given revision, which is usually a good idea, you just append the external version control system's usual revision flag. For example, if you wanted the Gdata Objective-C code pegged at revision 533, the first line in the .hgexternals file would look like this.
  
./contrib/gdata http://gdata-objectivec-client.googlecode.com/svn/trunk/ svn --revision 533
In order to get the hg externals command to act a little more like subversion, you'll want to add the hgexternals.extstatushook to your .hgrc to get the status of the external projects when you type hg stat. To do this, you add the below to your .hgrc as suggested in the hgexternals installation instructions.
[hooks]
post-status = python:hgexternals.extstatushook
With a couple of extra plugins, mercurial can pull from subversion and git, in addition to the usual mercurial repositories. If you prefer to let hgexternals shell out to git or svn (thus requiring you to use git and svn tools within the subprojects themselves), you can stop here. If you would like to use mercurial commands within all subprojects, regardless of whether they are Mercurial repositories, read on. Either way, we can now pull subprojects from anywhere and we can also use hg stat as we normally would in the working copy of the project and each subproject status will also be shown.

Fulfilling the last requirement is actually pretty simple. Mercurial has third-party plugins that do a fine job of allowing you to use mercurial inside of working copies pulled from git or subversion repositories. For git, hg-git from here does the trick. While you can always use git commands within the mogenerator subproject from the example above, I personally find it easier to avoid switching between different version control systems on the fly. Hg-git lets you to interact with a git repository using mercurial. In order to do that, we install hg-git and then change the third line in our .hgexternals file to the following.
./contrib/mogenerator git://github.com/rentzsch/mogenerator.git hg
What we're doing here is telling the mercurial hgexternals extension to pull from the git repository using mercurial itself. When hg-git is installed, this works with a git-based subproject just as it would with a Mercurial-based project. Make this edit to .hgexternals, remove the mogenerator directory that was checked-out with git, and rerun hg external command. Mercurial will now pull mogenerator from github and we can now work within the mogenerator subproject using regular hg commands.

A similar plugin called hgsubversion, available here, allows you to use Mercurial to pull from Subversion repositories. Just like we did with hg-git, we can install hgsubversion and then change our .hgexternals file to look like this.
./contrib/gdata svn+http://gdata-objectivec-client.googlecode.com/svn/trunk/ hg -r 533
./contrib/hgsubversion https://jonmarimba@bitbucket.org/durin42/hgsubversion hg
./contrib/mogenerator git://github.com/rentzsch/mogenerator.git hg
If we remove the gdata subdirectory tha wall pulled using Subversion from contrib and rerun hg external so that Mercurial will now pull it using its hgsubversion plugin, we now have three projects in our contrib directory that Mercurial itself pulled from git, svn, and hg repositories. With Mercurial plus the addition of a few plugins, we now have an über distributed version control system that makes it much simpler to work with and to update external code from many different sources.

September 26, 2010

My 2 cents in (hopefully) 5 minutes

saggau_speakerbadge.png

I'm performing a 5 minute "lightning" talk at the iPad/iPhone DevCon tomorrow and I'm not planning to use slides. I don't want to burn half of the 5 minutes getting keynote working, but hopefully attendees would like to have some of the links and code I'll reference. So! Here are my notes, links, and other such fodder. There is a lot more than 5 minutes worth of material here as I've been making notes about my habits for the last week or so; this is all of them. I plan to talk until I run out of air or out of time...

Tiny Little Habits for the iOS Dev to Avoid Premature Baldness

A foolish consistency is the hobgoblin of little minds, adored by little statesmen and philosophers and divines.
-- R.W. Emerson (Essays. First Series. Self-Reliance.)

Here are some consistencies that I think are not foolish. These little habits have saved me a lot of stupid, often difficult to debug, mistakes.

When you alloc and init or otherwise instantiate an object that you now own, write the balancing release or autorelease immediately; this includes any IBOutlets and properties. If it's an IBOutlet, also write the [self setSomeIBOutletProperty:nil] in -viewDidUnload when you write the [someIBOutlet release] in -dealloc.

Similarly

When something that is a delegate of something else, in the dealloc I'll tend to perform [somethingElse setDelegate:nil], whether I expect somethingElse to live for a while longer or not, then [somethingElse release]; on the next line. When you write [somethingElse setDelegate:something], write this code right away just like the alloc+init discipline above. This is one of the reasons I don't like to set delegates in xib files. It's too easy to mess this up. Also, I refactor as I work...

Speaking of delegates and protocols

When you write a delegate protocol, make absolutely every method optional so you get into the habit of testing if the delegate responds to the selector you're about to call as a matter of course. You're a lot less likely to mess up the delegate protocol that way. When you conform to a protocol, copy all (required and optional) methods into your class and comment them out. Uncomment them as you need to use them. That way, you don't have to go back and forth to the other class's header file to see if there is a delegate method for some behavior you need to know about. You have it all at hand.

Do as little setup work as possible in xibs, they work best for simply putting together UI geometry

  • Avoid xib file dependencies (See this BNR blog post) Avoid making view controllers in MainWindow.xib
    • It's easy enough to make 'em in code. It's easier to know where things come from when you make them in code.
  • Avoid setting delegates in xib files. Do it in code.
I'm not saying not to use xibs, they're useful and they often save time; I am saying to keep the interface builder voodoo to a minimum.

Lines of code are (mostly) free

Self-documenting code is better than heavily commented code and Objc is verbose by tradition; I think this is a good thing. Debugging code that isn't too nested is much easier.
[self setFloozle:[floozaWhat whatWithString:[NSString stringWithFormat:@"jeeminy %@ Christmas, %@", frickin, virginiaName]];
Where you gonna put a breakpoint in that crap?

When you think you can draw something in code instead of using a static image, try to find the time to implement drawing it in code. Also, Buy Opacity.app.
  • When apple comes out with yet another screen size, you'll be (somewhat more) ready for it.
  • When you want to change the size of something for some other reason, you don't have to generate a new image.
  • You get to figure out how on the green Earth you designer does the crazy things they do in photoshop... only in Quartz.

Speed

If the profiler doesn't say it's slow, it's not slow. If the profiler says it's slow, it's slow. Use the profiler.
Note: Shark (sniffle) is dead. Long live Shark! They killt it good in iOS 4.0 (no longer runs on the device).

Others' little code bits that are very useful:
  • Red Sweater NSData thing that prints the hex for you
  • IsEmpty from Wil Shipley with a small addition
    static inline BOOL IsEmpty(id thing) {
        return thing == nil
        || ([thing isEqual:[NSNull null]]) //My addition for things like coredata
        || ([thing respondsToSelector:@selector(length)]
            && [(NSData *)thing length] == 0)
        || ([thing respondsToSelector:@selector(count)]
            && [(NSArray *)thing count] == 0);
    }
    
  • LogMethod() from Aaron Hillegass
    //Modified from Aaron hillegass's code
    #define LogMethod() NSLog(@"-[%@ %s]", self, _cmd)
    
  • CPU and Memory tools I use to see what's really going on that I know I stole from a skojillian places, but can't remember who to thank.
  • GTMHTTPFetcher to download stuff (and Google Toolbox for Mac in general)
  • Nathan Eror's Core Animation Toys.

Subclassin' -- You stay classy, San Diego

Subclass less, compose more. In subclasses, always call super's implementation of a method, even when you just *know* it does nothing. (I'm thinking of UIViewControllers here). That way, if you ever do decide to put in an intermediary class between your class and the former superclass, you don't get stuck wondering what happened when those methods no longer get called.

Minimize direct access to the internals of your objects

Yeah, yeah, message send blah blah. It's not that slow unless you abuse it.
  • Write or synthesize accessors for ivars you want to share. Also, buy Accessorizer.app
  • Remember, you can use readonly in a property when appropriate (and it is more often appropriate than you might think).
  • Mark ivars you don't want messed with as @private. For srsly.
  • Make good use of class continuations:
    • to document the internal workings of your objects (If the header is the public documentation, the continuation is the private, implementation documentation)
    • to suppress "may not respond to" compiler warnings
    • to expand readonly properties to readwrite status internally
    • to make a protected methods header file if you want subclasses, but not other users of a class, to use certain of its internal methods.
      • Import that file in subclasses to suppress compiler warnings.
      • Makes it easy to delineate private vs. protected vs. public methods

Know what you're really doing when you use dot syntax

I have a weird dot syntax habit, ymmv. There be message sends in them thar hills.

I rarely return nil from something that is expected to return a string, dictionary, an array, or a set

Instead, I return an empty object of the expected type like so:
    return @"";
    return [NSArray array];
    return [NSDictionary dictionary];
I often use Shipley's IsEmpty() to find out if some object is empty, rather than comparing its pointer to nil. That way, nil messaging returning nil doesn't bite you in the ass when you're expecting something that is not an NSObject and you don't get that ugly (null) string in your UI. I hate that.

Make a logging method you can turn off and one you really can't; both call through to NSLog

Also, see the expansion of LogMethod from Aaron Hillegass
#define LogMethod() SBLog(@"-[%@ %s]", self, _cmd)
#define WarnMethod() SBWarn(@"-[%@ %s]", self, _cmd)
#define SBWarn NSLog

#ifdef DEBUG
#define SBLog NSLog
#else
#define SBLog    
#endif

Run the static analyzer, but not all the time

I usually run it:
  • before a commit to source control
  • after I make a new class
  • any time I am alloc'ing a lot of stuff
However, I don't find it particularly useful to set the preference that will run it for every compile; For me, the time spent on that adds up quickly. I have a lot of fairly big projects,though. YMMV.

If your code won't work unless some particular condition is satisfied, NSAssert that condition!

(Hi Joe P.) You can turn off asserts in shipping code, if you're a panzy. I leave 'em on. I would rather get a crashing bug I can reproduce than one I can't and it's probably gonna crash soon enough...

NO WARNINGS (and turn on the warning set you can find on the blog the Rentzsch linked to that one time)

On External, third party, projects

  • READ THE CODE
  • UNDERSTAND THE CODE
  • DON'T USE IT UNTIL YOU KNOW HOW IT WORKS (make a little test project to learn it and drop lots of breakpoints)
  • If it's hard to read, it's probably broken.
  • I'm on the three20 mailing list; you wouldn't believe some of the questions asked there...
  • Add external files project relative outside of your project (makes updating the external project easier)
  • Freeze external projects at a given revision number; update early on between releases:
    • in svn this is done with gtmOauth -r 19 http://gtm-oauth.googlecode.com/svn/trunk in an svn:external
  • Avoid editing external projects directly (makes it hard to merge), instead subclass or make categories to change behavior
  • When possible, avoid static libraries
    • Too many places where compiler flags are set.
    • Instead, compile in only those parts you need and their dependencies
    • I usually add just the top level source file I need and pull in dependencies as the compiler pukes, rinse and repeat until no errors or warnings.

Make tiny little test projects for new features

  • As your codebase grows, new features can present a difficult integration, so split it into two steps
    • 1. New Feature
    • 2. Integrate new feature into codebase
    • 3. goto 1

If there is any chance that you've got a delayed perform of a selector scheduled, don't forget to cancel those when appropriate

+[NSObject cancelPreviousPerformRequestsWithTarget:]
+[NSObject cancelPreviousPerformRequestsWithTarget:selector:object:]
Incolsolata, because menlo makes left square brackets look indented.

If you have to pick just one: Read Mike Ash's blog.

February 24, 2009

Useful bash foo with subversion

I've been a command-line user of subversion (svn) for some time and have long enjoyed these little bits of bash foo.

Stripping .svn directories

I often start one-off little test projects in my private slush svn repository and then move them into their own repository for further development as (or if) they grow up. Sometimes you'll want to get rid of svn's footprints in a working directory.
rm -rfv `find . -name *\.svn`
The find . -name *\.svn finds all of the .svn folders that svn uses to track the repository and with the help of those handy back-tics, rm -rv does the recursive removal of svn tracking directories.

Adding new files

During that early "making lots of new stuff" phase, I often generate quite a number of files that need to be added to svn at once. This is one of those times where having (say) class files in their own subdirectory is nice. Running this command will svn add every file that isn't currently being tracked by svn in the current directory.
svn add `svn stat |grep \? |awk '{print $2}'`
Grep finds every svn stat output line that includes a question mark (meaning the item is not currently under version control) and pipes it through awk '{print $2}', which shows only the text (the filename) from the second column of the svn stat output. The back-tics and svn add finish the magic.

Using a Mulligan

I'll use something similar to the above along with svn revert to rollback a working copy "all the way." Say you're experimenting in your working directory; you've added a few files, hit a dead end, and want to revert the whole tree as well as delete anything you've added. In other words, you're looking for a full-on "do over." Using svn revert --recursive will revert any files that svn is tracking, but will leave anything svn is not tracking alone, so we have to also remove those files.
svn revert --recursive && rm -rf `svn stat |grep \? |awk '{print $2}'`

As with any other nerd foo, make sure that you really want to do what you're asking for when you use these commands. These make my life with svn just a little easier.

November 14, 2008

Reverse DNS on the iPhone

Reverse DNS via the usual OS X means doesn't seem to work on the iPhone. It looks like this is a known bug/limitation. The new apple developer forums (login required) have a thread that's dedicated to the problem. (rdar://problem/5929766 is mentioned there). Apple seems to have used the eraser on some other of their DNS code on the iPhone. Saurik has an interesting hack to work - around a perhaps related change in DNS behavior.

You can actually get reverse DNS lookup on the iPhone to work using res_query (which is in libresolv, so make sure you link against libresolv.dylib) to query DNS and dns_parse_packet, which you'll find in dns_utils.h, to do the work of parsing out the DNS server reply works rather well. I could not get the recommended tools in dns.h to work, but I did discover that res_query returns the same raw reply from the DNS Server that dns_parse_packet expects from the utilities in dns.h. The code:

Continue reading "Reverse DNS on the iPhone" »

September 14, 2008

Using Shark and custom DTrace probes to debug Nagios on Mac OS X

Nagios has for several years represented a favorite wrench in my networking toolbox. It does a fantastic job of monitoring various hosts and services, warning the sysadmin if things start to get wonky, usually well before any user notices a change in service. It compiles and runs cleanly and has always performed like a champ for me. I wouldn't want to run a server without it.

I recently purchased a Mac Mini to use as a small portable network monitor for those occasions when I require some short-term network monitoring. While I strongly considered installing Linux on it, as that's what I usually use for this sort of thing, I decided to build the tools I needed in OS X Leopard. I use Leopard Server at work (I love the newiCal server), and since Leopard is officially Unix compliant, I didn't expect too much trouble, especially with great sources of FOSS for the Mac like MacPorts.

I installed Nagios through Macports (sudo port install Nagios), configured a few hosts to check, and tested the warning system. Everything was happily up and running and I had moved on to downloading and installing ntop and a few other tools when I noticed the CPU fan spooling up. Running top indicated that nagios was spinning a whole processor core. Hmm.

Google tells that others have seen this problem, but I could find no solution online. Let's find out why this is happening.

Continue reading "Using Shark and custom DTrace probes to debug Nagios on Mac OS X" »

December 09, 2007

Always - on Print Preview

Some software is, in my experience, not quite WYSIWYG when it comes to the final printed output so I have gotten into the habit of viewing a PDF in preview before I actually send a document to my printer. This has saved many trees.

Apple makes it fairly easy to do this. Just command-P and click on the weird-looking PDF button to get the drop down menu to select "Open PDF In Preview."

preview.jpg

I would like to see most every print job automatically open in preview without needing to take my hands off of the keyboard. One can make this happen with a little bit of effort, some help from Folder Actions, and a little piece of free third-party software. Here's how I do it.

First, download and install Cups PDF per the instructions on its home page. This will create a printer and driver that dumps a pdf file into ~/Desktop/cups-pdf/. Set your default printer to CUPS-PDF.

Next, you will setup a folder action on the ~/Desktop/cups-pdf/ to open each file that gets dumped in there in preview. I keep my folder actions scripts in ~/Desktop/Folder\ Actions/, so I put the following applescript (derived from a similar script that ships with Leopard) there.

  
(*
add - new item alert

This Folder Action handler is triggered whenever items are added to the attached folder.
The script will display an alert containing the number of items added and offering the user
the option to reveal the added items in Finder.

Copyright © 2002–2007 Apple Inc.

You may incorporate this Apple sample code into your program(s) without
restriction. This Apple sample code has been provided "AS IS" and the
responsibility for its operation is yours. You are not permitted to
redistribute this Apple sample code as "Apple sample code" after having
made changes. If you're going to redistribute the code, we require
that you make it clear that the code was descended from Apple sample
code, but that you've made changes.
*)

property dialog_timeout : 5 -- set the amount of time before dialogs auto-answer.

on adding folder items to this_folder after receiving added_items
try
tell application "Finder"
--get the name of the folder
set the folder_name to the name of this_folder
end tell

-- find out how many new items have been placed in the folder
set the item_count to the number of items in the added_items
--create the alert string
set alert_message to ("Folder Actions Alert:" & return & return) as Unicode text
if the item_count is greater than 1 then
set alert_message to alert_message & (the item_count as text) & " new items have "
else
set alert_message to alert_message & "One new item has "
end if
set alert_message to alert_message & "been placed in folder " & «data utxt201C» & the folder_name & «data utxt201D» & "."
set the alert_message to (the alert_message & return & return & "Would you like to view the added items?")

display dialog the alert_message buttons {"Yes", "No"} default button 1 with icon 1 giving up after dialog_timeout
set the user_choice to the button returned of the result

if user_choice is "Yes" then
tell application "Preview"
--fire it up
activate
--open the items
open the added_items
end tell
end if
end try
end adding folder items to

Ctrl (or right) click the cups-pdf folder, Enable Folder Actions, then Configure Folder Actions via the contextual menues seen below.


EnableFolderActions.jpg

When you select "Configure Folder Actions" you'll set the script that runs every time a new file is added to that folder to the one above.

folderActions.jpg


Now every time you hit command - P to print in a application, you'll hit enter to print to your default CUPS-PDF printer, which will write a PDF to the cups-pdf folder on your desktop. The folder action will then pop-up this window (for five seconds).

PreviewMe.jpg


All you have to do is hit enter.

So! Every time I print I hit command - P, enter, wait a sec, enter. Instant preview.

November 11, 2007

More Leopard calDAV fun. Migration!

As you know from my last post, I have lots and lots of calendars. It used to be much much worse. Sharing calendars between my three Macs before Leopard server was a pain in the ass. Sure, you could put a calendar on a regular DAV server and subscribe to that calendar on your other machines, but you could not write to that calendar from any machine other than that which created it initially. All subscribed machines were read-only. What does this mean to me? You know about my 14 calendars, right? Triple it. That's right. I used to have 42 calendars on each of my 3 machines. 14 calendars to which I could write and 28 to which I was subscribed so I could read any appointments from my other two machines. What a mess.

Leopard calendar server has brought me back to a debatably sane 14 calendars. But how does one migrate all of those calendars spread across multiple machines onto the shiny new calDAV server? Like so:

1. Subscribe to your shiny new calDAV server (under the Accounts tab) in iCal preferences.
2. Create a new calendar on your calDAV server through iCal (File Menu -> new Calendar -> your server) for each of your 14 calendars.
3. Create a *.ics file from your old calendar by:
a. Selecting the calendars you want to export by using the export functionality in iCal (File Menu -> Export) on each of your machines or...
b. Mounting your DAV server and downloading all of 'em at once (assuming you're keeping your calendars on a DAV server now, which I was, this method is a lot easier for obvious reasons)...
4. Select the calendar from the source list (one residing on your new calDAV server) and import the *.ics files you wish to store in the new calendar (File Menu -> import).

Your appointments are now on your calDAV server and will show up on all of your subscribed machines fully editable from all.

More Leopard calDAV fun. Moving appointments between calendars from different sources.

I'm scheduled to within an inch of my life, so there is no surprise that I have 14 calendars in iCal right now. I have calendars for billable hours vs. non-billable hours. I have calendars on my calDAV server that I share with clients, some under a different username than others, and some that I use to synchronize personal calendars across multiple machines, and yet another that I share with my assistant. (Hi Christina!) I have calendars coming out my ears from various sources.

Very often, I'll place an appointment in one calendar and later wish to move it to another. Perhaps I'll have a tentative appointment with a client, so I'll put that in my non-billable calendar. When that appointment happens, perhaps I'll put that into my billable calendar. Unfortunately, my billable calender is under a different username on my calDAV server than my billable calendar (one of them I share, the other I do not). Normally, when I want to move an appointment from one calendar to another I right/ctrl-click the appointment and select the calendar I want to move it to. Unfortunately, the only calendars listed in that contextual menu are those under the same user account as the appointment I'm trying to move. Bummer.

The workaround:

1. Select the appointment you would like to move.
2. Use your favorite method of getting it to the clipboard (I use command-X to cut)
3. Select the calendar from the source list to which you would like to move the appointment
4. Paste (command - v for me)

All appointment data when cut or copied is retained when pasted into the other calendar.

Wouldn't it be nice to have ALL of your calendars in the contextual menu? Radar: 5593635

November 06, 2007

Leopard server, calDAV, and Mozilla Sunbird

For those wishing to use Mozilla Sunbird with the calDAV server that comes with Leopard, it's not setup in Sunbird in (quite) the same way as it is in iCal.

Let's say you have the calDAV Account URL working in ical (which will subscribe you to all of your calendars) and its URL is

 https://somedomain.com:8443/principals/users/username/ 

In Sunbird, you'll have to subscribe to the your calendars individually. Every calDAV user I've setup so far in Leopard server has been given a default calendar called simply "calendar", which you can subscribed to from Sunbird at

https://somedomain.com:8443/calendars/users/username/calendar/
Easy enough. However, if you add a new calendar in iCal, it will create a calendar named with a UUID. In order to figure out what that UUID is, it's easiest to just mount the calDAV in the finder (Command - K) at
https://somedomain.com:8443/calendars/users/username/
You'll see several directories in there: calendar, dropbox, oubox, notifications, etc. The one that looks like a UUID (for example, 037D3206-9C1D-4C6C-8E54-B3E3CAF90ABF) is the directory you'll want to subscribe to in Sunbird. In the example above, you would subscribe to
https://somedomain.com:8443/calendars/users/username/037D3206-9C1D-4C6C-8E54-B3E3CAF90ABF/

August 22, 2007

Carburetor Tuning

Sent in an email recently.

Carburetor tuning:

Even when you're doing it right, anything you change in one system will often screw something up in another unexpectedly.

Sometimes starting over from a reasonable baseline is better than hammering away at a not-quite-right but oh-so-close-to-working plan.

Keeping track of what you've done makes it easier to get where you want to be, even if what you've done has sucked grapes.

...funny. Writing music works the same way, too... and so does writing code.

August 14, 2007

iPhone Native Pong Application: the real "sweet" SDK

The native iPhone hack I put together for the C4[1] conference is in the process of a "wow, I hope somebody can read this code someday" cleanup. I will release the source in the coming days. Stay tuned for a download link.

In the meantime, here are a couple of teasers.

Screenshots:

scrnsht1.jpg


scrnsht2.jpg


And here is some code for playing a sound file from your native iPhone Application's bundle (with the class-dump'd headers you'll need, too). The iPhone, as was mentioned by everyone with a native hack at C4, has some really intelligent API. Hats off, ladies and gentlemen, to the iPhone team. They must be having a grand ol' time internally playing with this thing.



//
// PongAudio.h
//
// Created by Jonathan Saggau on 2007-08-12.
// Copyright (c) Jonathan Saggau All rights reserved.
//

#import <Foundation/Foundation.h>
@class AVItem;
@class AVController;
@class AVQueue;

@interface PongAudio : NSObject
{
AVItem *bounce;
AVItem *loser;
AVQueue *q;
AVController *controller;
}

-(void)playBounce;
-(void)playLoser;

-(void)stop;
@end

//
// PongAudio.m
//
// Created by Jonathan Saggau on 2007-08-12.
// Copyright (c) 2007 __MyCompanyName__. All rights reserved.
//

#import "PongAudio.h"
#import "AVItem.h"
#import "AVController.h"
#import "AVQueue.h"

//From Aaron hillegass
#define LogMethod() NSLog(@"-[%@ %s]", self, _cmd)

@interface PongAudio (PrivateAPI)
-(void)play:(AVItem *)item;
@end

@implementation PongAudio


- (id)init
{
self = [super init];
if (nil!= self)
{
NSError *err;
NSString *path = [[NSBundle mainBundle] pathForResource:@"PongBounce" ofType:@"wav" inDirectory:@"/"];
bounce = [[AVItem alloc] initWithPath:path error:&err];
if (nil != err)
{
NSLog(@"err! = %@ \n item = [[AVItem alloc] initWithPath:path error:&err];", err);
exit(1);
}

path = [[NSBundle mainBundle] pathForResource:@"PongLose" ofType:@"wav" inDirectory:@"/"];
loser = [[AVItem alloc] initWithPath:path error:&err];
if (nil != err)
{
NSLog(@"err! = %@ \n item = [[AVItem alloc] initWithPath:path error:&err];", err);
exit(1);
}

controller = [[AVController alloc] init];
[controller setDelegate:self];
q = [[AVQueue alloc] init];
[q appendItem:bounce error:&err];
if (nil != err)
{
NSLog(@"err! = %@ \n [q appendItem:item error:&err];", err);
exit(1);
}

[q appendItem:loser error:&err];
if (nil != err)
{
NSLog(@"err! = %@ \n [q appendItem:item error:&err];", err);
exit(1);
}
}
return self;
}

- (void)dealloc
{
[bounce release]; bounce = nil;
[loser release]; loser = nil;
[q release]; q = nil;
[controller release]; controller = nil;
[super dealloc];
}

-(void)playBounce
{
[self play:bounce];
}

-(void)playLoser
{
[self play:loser];
}

-(void)play:(AVItem *)item;
{
[controller setCurrentItem:item];

//play NOW
[controller setCurrentTime:(double)0.0];
//should probably check this eventually, too.
//- (BOOL)isCurrentItemReady;
NSError *err;
[controller play:&err];
if(nil != err)
{
NSLog(@"err! = %@ [controller play:&err];", err);
exit(1);
}
}

-(void)stop;
{
[controller pause];
}

- (void)queueItemWasAdded:(id)fp8
{
LogMethod();
}
@end


//AVController.h
/*
* Generated by class-dump 3.1.1.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
*/


#import <Foundation/Foundation.h>
#import "CDStructures.h"

@interface AVController : NSObject
{
struct AVControllerPrivate *_priv;
}

+ (void)setEnableNetworkMode:(BOOL)fp8;
+ (id)avController;
+ (id)avControllerWithQueue:(id)fp8 error:(id *)fp12;
- (id)initWithError:(id *)fp8;
- (void)setAVItemClass:(Class)fp8;
- (id)init;
- (void)dealloc;
- (struct AVControllerPrivate *)privateStorage;
- (BOOL)isNewImageAvailableForTime:(const CDAnonymousStruct1 *)fp8 willNeverBeAvailable:(char *)fp12;
- (long)copyImageForTime:(struct __CVBuffer **)fp8 time:(const CDAnonymousStruct1 *)fp12;
- (void)itemPropertyDidChangeNotification:(id)fp8;
- (void)scheduleQueueSpaceCheck;
- (void)checkQueueSpace;
- (void)queueItemWasAdded:(id)fp8;
- (void)queueItemWasAddedNotification:(id)fp8;
- (void)queueItemWillBeRemovedNotification:(id)fp8;
- (void)cancelPrepareForPlayback;
- (void)setQueue:(id)fp8;
- (id)queue;
- (void)feederRangeWasInserted:(id)fp8;
- (void)feederRangeWasRemoved:(id)fp8;
- (void)feederInvalidatedWithCurrentItemMoved:(id)fp8;
- (void)setQueueFeeder:(id)fp8 withIndex:(int)fp12;
- (void)setQueueFeeder:(id)fp8;
- (id)queueFeeder;
- (BOOL)setRepeatMode:(int)fp8;
- (int)repeatMode;
- (BOOL)havePlayedCurrentItem;
- (id)currentItem;
- (id)currentItemPriv;
- (void)setCurrentItem:(id)fp8 preservingRate:(BOOL)fp12;
- (void)setCurrentItem:(id)fp8;
- (void)makeCurrentItemReady;
- (BOOL)isCurrentItemReady;
- (void)continueAfterRepeatGap;
- (void)cancelContinueAfterRepeatGap;
- (void)doCancelContinueAfterRepeatGap;
- (void)doScheduleContinueAfterRepeatGap:(id)fp8;
- (BOOL)beginRepeatGap;
- (void)itemFailedPlaying;
- (void)itemFinishedPlaying:(id)fp8;
- (void)itemCompletedDecode;
- (BOOL)play:(id *)fp8;
- (void)pause;
- (void)dequeueFirstItem;
- (unsigned int)indexOfCurrentQueueFeederItem;
- (BOOL)setIndexOfCurrentQueueFeederItem:(unsigned int)fp8 error:(id *)fp12;
- (id)addNextFeederItemToQueue;
- (BOOL)playNextItem:(id *)fp8;
- (float)rate;
- (BOOL)shouldBeginPlayingItem:(id)fp8 error:(id *)fp12;
- (BOOL)setRate:(float)fp8 error:(id *)fp12;
- (BOOL)resumePlayback:(double)fp8 error:(id *)fp16;
- (id)errorWithDescription:(id)fp8 code:(long)fp12;
- (void)makeError:(id *)fp8 withDescription:(id)fp12 code:(long)fp16;
- (BOOL)beginInterruption:(id *)fp8;
- (BOOL)activate:(id *)fp8;
- (void)endInterruptionWithStatus:(id)fp8;
- (float)volume;
- (void)setVolume:(float)fp8;
- (double)currentTime;
- (void)setCurrentTime:(double)fp8;
- (BOOL)muted;
- (void)setMuted:(BOOL)fp8;
- (void)setEQPreset:(int)fp8;
- (int)eqPreset;
- (struct OpaqueFigVisualContext *)visualContext;
- (void)setVisualContext:(struct OpaqueFigVisualContext *)fp8;
- (id)outputQTESFilePath;
- (void)setOutputQTESFilePath:(id)fp8;
- (id)lkLayer;
- (void)setLayer:(id)fp8;
- (id)attributeForKey:(id)fp8;
- (BOOL)setAttribute:(id)fp8 forKey:(id)fp12 error:(id *)fp16;
- (struct _LKImageQueue *)lkImageQueue;
- (struct _LKImageQueue *)lkEnsureQueueForWidth:(unsigned int)fp8 Height:(unsigned int)fp12;
- (double)lkServerTime;
- (BOOL)okToNotifyFromThisThread;
- (void)fmpTimeJumped;
- (void)fmpRateDidChange;
- (void)rateDidChangeToRate:(float)fp8;
- (void)repeatModeHasChanged:(int)fp8;
- (void)currentItemWillChangeToItem:(id)fp8 oldItemCurrentTime:(double)fp12;
- (void)currentItemHasChanged:(id)fp8;
- (void)itemHasFinishedPlayingNotification:(id)fp8;
- (void)resynchronizeTiming;
- (id)delegate;
- (void)setDelegate:(id)fp8;
- (BOOL)setItemAttribute:(id)fp8 value:(id)fp12 forKey:(id)fp16 error:(id *)fp20;
- (id)itemAttribute:(id)fp8 forKey:(id)fp12;
- (id)initWithQueue:(id)fp8 error:(id *)fp12;
- (BOOL)isValid;
- (id)initWithQueue:(id)fp8 fmpType:(unsigned long)fp12 error:(id *)fp16;
- (void)applyAttributesFromItem:(id)fp8;
- (void)fmpRelease:(id)fp8;
- (void)failPlayback:(id)fp8 reason:(long)fp12 notifyClient:(unsigned char)fp16;
- (void)prepareForPlaybackReply:(long)fp8;
- (int)instantiateFMPRef:(struct opaqueFigMoviePlaybackRef **)fp8 forItem:(id)fp12;
- (void)maybeDumpPerformanceDictionary:(struct opaqueFigMoviePlaybackRef *)fp8;
- (void)removeFMPRefListeners:(struct opaqueFigMoviePlaybackRef *)fp8;
- (void)shutdownFMPRef:(struct opaqueFigMoviePlaybackRef *)fp8;
- (void)updateTimeMarkerObservations;
- (void)scheduleUpdateTimeMarkerObservations;
- (void)registerTimeMarkerObserver:(id)fp8 forItem:(id)fp12 times:(id)fp16 context:(id)fp20;
- (void)removeObserver:(id)fp8 fromTMOArray:(id)fp12;
- (void)unregisterTimeMarkerObserver:(id)fp8 forItem:(id)fp12;

@end

//AVExternalAudio.h
/*
* Generated by class-dump 3.1.1.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
*/

#import <Foundation/Foundation.h>
#import "CDStructures.h"

@interface AVExternalAudio : NSObject
{
struct AVExternalAudioPrivate *_priv;
}

+ (id)avExternalAudio:(id)fp8;
- (id)initWithDelegate:(id)fp8;
- (void)dealloc;
- (id)attributeForKey:(id)fp8;
- (BOOL)setAttribute:(id)fp8 forKey:(id)fp12 error:(id *)fp16;
- (void)makeError:(id *)fp8 withDescription:(id)fp12 code:(long)fp16;
- (void)postServerConnectionDiedNotification:(id)fp8;
- (void)fmpServerConnectionDied;
- (BOOL)okToNotifyFromThisThread;
- (BOOL)activate:(id *)fp8;
- (float)volume;
- (BOOL)isActive;
- (void)postUserVolumeChangedNotification:(id)fp8;
- (void)fmpUserVolumeDidChange;
- (void)fmpChangeConnectionActive:(BOOL)fp8;

@end

//AVItem.h
/*
* Generated by class-dump 3.1.1.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
*/

#import <Foundation/Foundation.h>
#import "CDStructures.h"

@interface AVItem : NSObject
{
struct AVItemPrivate *_priv;
}

+ (id)avItem;
+ (id)avItemWithPath:(id)fp8 error:(id *)fp12;
- (id)initWithError:(id *)fp8;
- (id)init;
- (id)initWithPath:(id)fp8 error:(id *)fp12;
- (void)dealloc;
- (BOOL)setPath:(id)fp8 error:(id *)fp12;
- (int)_instantiateItem;
- (id)path;
- (double)duration;
- (struct CGSize)naturalSize;
- (float)volume;
- (void)setVolume:(float)fp8;
- (void)setEQPreset:(int)fp8;
- (int)eqPreset;
- (id)attributeForKey:(id)fp8;
- (BOOL)setAttribute:(id)fp8 forKey:(id)fp12 error:(id *)fp16;

@end

//AVQueue.h
/*
* Generated by class-dump 3.1.1.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
*/

#import <Foundation/Foundation.h>

@class NSMutableArray, NSRecursiveLock;

@interface AVQueue : NSObject
{
NSRecursiveLock *_mutex;
NSMutableArray *_items;
void *_reserved;
unsigned int _flags;
}

+ (id)avQueue;
+ (id)queueWithArray:(id)fp8 error:(id *)fp12;
- (id)initWithError:(id *)fp8;
- (id)init;
- (id)initWithArray:(id)fp8 error:(id *)fp12;
- (void)dealloc;
- (int)_instantiateItem;
- (unsigned int)itemCount;
- (id)itemAtIndex:(unsigned int)fp8;
- (unsigned int)indexOfItem:(id)fp8;
- (id)itemAfterItem:(id)fp8;
- (BOOL)appendItemsFromArray:(id)fp8 error:(id *)fp12;
- (BOOL)appendItem:(id)fp8 error:(id *)fp12;
- (void)itemWasAdded:(id)fp8;
- (BOOL)insertItem:(id)fp8 atIndex:(unsigned int)fp12 error:(id *)fp16;
- (BOOL)insertItem:(id)fp8 afterItem:(id)fp12 error:(id *)fp16;
- (void)itemWillBeRemoved:(id)fp8;
- (BOOL)removeItem:(id)fp8;
- (BOOL)removeItemAtIndex:(unsigned int)fp8;
- (void)removeItemsInRange:(struct _NSRange)fp8;
- (void)removeAllItems;

@end

January 02, 2007

Google Analytics follow-up

I found an easy way to add Google Analytics to a movabletype blog as well. There is a small plugin that puts the javascript on your page available here. I love it when others write code so I don't have to.

December 31, 2006

Google Analytics + apache configuration mini - howto

I’ve been thinking of setting up google analytics on my site for a while now and have finally taken a few moments to get started. I'm curious to see where folks are coming from and am kind of tired of writing one-off scripts to grep my apache logs.

If, like me, you have a site made of many many html pages, this one’s for you. (DO please make a backup of your site and configuration files before you start).

In order for google to analyze a given web page, you need to add a short javascript to each. It looks something like this:


<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "XX-XXXXXXX-X";
urchinTracker();
</script>

In order to easily add this text the each of my pages, I chose to use Apache's 'Server Side Includes . This is an easy way to get a chunk of text to be included in many files. Put the javascript that google gives you into a file called (say) includes.html and drop it in your apache document root. (I chose to put mine in my /data/ subdirectory as it works best for the structure of my site).

Each html document you want google to track will need to include that file’s text immediately before the </body> tag through the placement of a small SSI tag.

It will look something like this:


<!--#include virtual="/data/included.html" -->

To do such a massive replace, we’ll need a little Perl (yes, Juan PERL!) and bash.

perl -p -i -e 's/<body<!--#include virtual=\"\/data\/included.html\" -->\n <body/g' `find . |grep \.html`

This finds all html files, then performs a mass insertion before the </body> tag with the SSI include. Apache will take the include and replace it with the contents of the file.

If SSI isn't yet configured on your apache box, there is a simple set of tricks that will make life MUCH easier. Assuming that you have compiled mod_include (most apache installations have this), you simply need to add

 Options +Includes 
to your site configuration. I placed it in my virtual hosts file along with my other Options directives. The real juice is here: place
 XBitHack on 
in your apache configuration as well. This will make apache parse any file (looking for SSI includes) with the execute (remember your unix permissions?) bit enabled. You will then set the execute bit on all of your html pages like so:

cd your/apache/htdocs
chmod +x `find . |grep \.html`

That should do it. We have written a small includes.html file to contain google's javascript, done a massive search and insert on all of your html files, setup apache to use SSI with XBitHack, and changed the execute bit of the html files so apache will parse them. Now, when you look at the source of your page as served from apache, it should include the javascript from the includes.html file within each page.

Note: XBitHack will not work on windows for obvious reasons. There is no executable bit on windows. See the apache tutorial referenced above for other methods to enable SSI if you're on windows.

November 04, 2006

Portfile as howto

I had an interesting Apache gdb session recently wherein I discovered that the Apache Portable Runtime (APR) package on one of my clients' shiny new Debian Linux boxes was.... well ... completely borked, causing a segfault for every apache child process. It's my fault for using the testing distribution, but (hey!) the box isn't all that important and it's an excuse to see what's new in Debian-land. However, as I actually do want to serve a web page from this machine, I figured it best not to further trust the package manager to find me non-broken software. I decided to download APR and friends + apache and compile it myself. The last time I did anything like this, I used MacPorts (then darwinports) to compile and install apache on my mac at home, a very "batteries included" affair as MacPorts selects a good set of default options for me. After some attempts to grok the various configuration options for Apache, I decided that I really just wanted the same options I have installed on the MacPorts+OSX box at home. Enter MacPorts happily human readable portfiles.

In the MacPorts portfile for apache we find the configure.args line.

configure.args --with-apr=${prefix}/bin/apr-1-config --with-apr-util=${prefix}/bin/apu-1-config --libdir=${prefix}/lib --with-expat=${prefix} --with-pcre=${prefix} --mandir=${prefix}/share/man --enable-mods-shared=all --enable-ssl --with-ssl=${prefix} --enable-deflate --enable-proxy --enable-proxy-connect --enable-proxy-http --enable-proxy-ftp


All that was necessary was to make the prefix variable point to where I wanted to install apache, drop in the args above for the ./configure script, and everything compiled without a hitch. I did already have the various other dependencies (ssl, etc.) installed on my system from the aborted attempts at installing the Apache2 package, so there were no surprises from missing libraries. Editing the (happily very clear and atomic) default apache configuration files a little brought me to the necessary service configuration very quickly. Now I have virtual hosting, mod_rewrite (to send all requests on port 80 to the https port), and some other nice features running on the very latest stable Apache2.

September 23, 2006

Reality

"Reality isn't virtual enough."

Seriously. I just heard this at a game design panel.

September 06, 2006

Nike data is smooooooooooth like budda

Looks like the Nike+ data gets... um ... just a little smoothing / averaging. Then again, I still need to test the data importer... You're looking at distance reported per 10 second interval. Raw data babeeee.


run.jpg

July 04, 2006

Parallels' problem isn't case-sensitivity.

With the help of Drew Thaler's instructions on how to grab disk access by running an app in gdb, it looks like the bug in Parallels Workstation was even weirder than the case-sensitivity problem. Parallels uses QT, a cross-platform GUI toolkit to handle user interface presentation, rather than the much-more-common-on-OSX-but-not-cross-platform toolkit called cocoa. Unfortunately, it follows my library search path to get to QT. My installation of QT is broken. (I've known this for a while and haven't had the time to rebuild it from source). Why doesn't Parallels force inclusion of QT from the app bundle? I don't know. Bug report filed.

See the extended entry for Drew's instructions (thanks Drew!)

Continue reading "Parallels' problem isn't case-sensitivity." »

June 07, 2006

Bob

I don't know how he finds the time, but the python / mac community owes Bob several rounds of applause. Let me be the first to stand (soon as I get this here foot outta mah mouth). I'm also wowed at how he managed to find my blog and comment before I got round to sending a question to the mailing lists. And ... well you know... he kicked my butt and I deserved it.

April 30, 2006

NSZombie update

I wonder if I'm using cocoa bindings incorrectly... or maybe there's a bug. Usually when I start to think "maybe there's a bug in somebody else's code," I look for ways in which I'm using that code in weird (wrong) ways. I figure that the pyobjc people are about 7 orders of magnitude better at this than I am.

Continue reading "NSZombie update" »

April 10, 2006

NSZombie eating my brain!

I've been learning cocoa bindings (I like this voodoo) and have hit a brick wall. I've been asking pythonmac-sig if they've any ideas and they pointed me in the direction of NSZombieEnabled, but i haven't been able to get very far.

I have an NSArray controller with a python object subclassing NSObject. I've put a + button on the gui to add the python object like this:::

def addTriggerModel_(self, sender): # BUTTON
self.setCanAddTriggerModel_(False)
newTriggerModel = scNSDrumTriggerModel.alloc().init()
newTriggerModel.resetWithNodeBusAndInput(node = self.nextNode, bus = self.nextBus, \
input = self.nextInput, sendInfo = self.sendingTriggersToClient)
self.nextNode += 100
self.nextBus += 10
self.nextInput += 1
NSLog("Adding newTriggerModel")
self.triggerArrayController.addObject_(newTriggerModel)
NSLog("Added newTriggerModel")
self.setCanAddTriggerModel_(True)


This works just peachy the first time I do it, but fails on a second addition of the new scNSDrumTriggerModel object. At first I was getting Signal 5/6 errors, then I did:

from PyObjCTools import Signals
Signals.dumpStackOnFatalSignal()
from PyObjCTools import Debugging
Debugging.installVerboseExceptionHandler()

This told me:

NSGenericException - *** Selector 'retain' sent to dealloced instance 0x1147cb0 of class OC_PythonArray

So I turned on NSZombieEnabled and tried running in gdb with a breakpoint on -[_NSZombie retain], but I don't really know what I'm looking for. I'm looking at a lot of hex memory addresses with very little human-readable stuff in there.

I tried setting a breakpoint at (+[OC_PythonArray newWithPythonObject:]) to see perhaps what proxy objects are being instantiated, but I'm clearly in over my depth. The variable corresponding to the pythonObject in OC_PythonArray newWithPythonObject: up there ^ has address 0x0 (Is that a null pointer?) in gdb each time I hit that breakpoint.

When I run malloc_history on the memory address that the NSZombie has, I get this:

Continue reading "NSZombie eating my brain!" »

January 27, 2006

Comments

Anyone who has tried to comment on my 'blog, please try again.  My server was blocking outgoing smtp and I wasn't getting the notifications to approve comments.

oops.

January 21, 2006

Sometimes Being a Nerd is NOT_FUN_(aka.the_suck())

So I've been going 'round and 'round (including sending a DVD-R via UPS RED which - I'm told - was corrupted upon arrival) to get a binary database save file to the company that handles the database maintenance at one of my clients so that they can perform an upgrade to the system.

I finally bit the bullet... and did everything I could think of to make sure that the file got there intact:

Continue reading "Sometimes Being a Nerd is NOT_FUN_(aka.the_suck())" »

January 18, 2006

python performance suck

Stupid nerd trick:  (File under... DUH!)

When working on real-time audio control, it's probably not a bad idea to try to get the best possible time-slicing quantum possible.  What with latency to think about and generally wanting everything to just-happen-right-the-heck-now(TM)...

Continue reading "python performance suck" »