Navigation

Recent site activity

External Links

My Personal Blog


Apology

posted Aug 5, 2011 8:37 PM by Theresa McClain

To Whom It May Concern:

Last weekend, as a friend and I were moving into a house together, two members from the Jehovah's Witness Church knocked on our door.  Being the jerk I can often be, I welcomed the opportunity to...  I’m not even sure what my goal was; I can only say that from almost every body's perspective but my own, I was an asshole.

I acted interested in what they had to say, just long enough to spring on them with rehearsed logic, accusations, and a cowardly defensiveness against an unsuspecting, undeserved adversary.  I am both embarrassed and ashamed of my behavior.

Any religious person who honestly believes the faith they adhere to, would be selfish not to want to share what they believe.  I can only aspire to finding a truth so convicting that I feel compelled to spread it door-to-door.

I logically disagree with them about the truth of their religious claims; I feel strongly that faith unchecked is one of the most dangerous forces humanity has to contend with.  I am horrified at the abuses human beings have endured as a result of religious beliefs, both currently and historically.  Make no mistake:  I believe that the world would be a better place without religion.

Still, without belief in a creator, I am only accountable to my peers and myself.  Everything I choose to do or say can make the world a better place, or it can do the opposite.  I want to be a person who makes the world a better place; I value things in this world which promote happiness and peace.  I understand both logically and emotionally why the Golden Rule is a good rule to live by.

My behavior during the conversation with the two Jehovah’s Witnesses who approached me did not make the world a better place.  I arrogantly plucked out my mobile phone towards the end of the conversation and offered to record any “proof” they might have that God exists.  As I began recording the conversation with the video app on my phone, I explained that I would put it on the Internet for everyone to see.  I never felt entirely comfortable putting it on the Internet.  When I uploaded it to YouTube I was still trying to convince myself that it was the right thing to do.  I now know in hindsight that it was wrong. I have deleted the video.

If ever I have owed an apology, this is one of those times.  I intend to give this letter to the pastor of the nearest Jehovah's Witness Church so that hopefully the two men who approached me will know that I am sorry.

Additionally, I apologize to all the people in my life who I have challenged, often arrogantly and insensitively, from both sides of the religion/science debates, for my behavior.

I am sorry.

Presence 3

posted Dec 9, 2009 8:22 AM by Theresa McClain

Presence 3 is *almost* finished.  I've completed all the requirements, and am working on some of the extra credit as well as some enhancements of my own that I've come up with and decided to implement.  One of the extra credit items was to show each friend's most recent status underneath their display name in PersonListView - I did that, and didn't like it as much as having their username displayed.  I know that in Presence 4 we're going to add a few modes, and it kind of seems like seeing your friends' status updates in a list should belong in a mode, available by selecting a tab.  I don't know - we'll see what happens in Presence 4.

Here are a few enhancements I'd like to work on:

  • Add the ability to tweet directly to one of the people in your friends list from the detailTableView.  This would add something like @username at the beginning of the tweet text, already filled in for you before you start typing.  The TweetView nib file is already finished, so that should be easy.
  • Provide the ability to view a friend's list of friends by pushing another tableview onto the navigation stack from the detailView.
  • Change the settings nib file so that it uses a more standard looking table view to show the username and password.  Maybe there are other configuration options to put here, as well?
  • Make the username textfield active (give it focus) when that view is first displayed to the user.  This should be really easy.
  • Make the tweet text TextView control active (give it focus) when that view is first displayed.  Again, that should be really easy.

I'm not going to spend a LOT of time enhancing this as I want to get onto the final project, which a friend and I are working on together.  :)

Reviewing UINavigationController

posted Dec 7, 2009 7:50 AM by Theresa McClain

When I built Presence 2, I made a fairly significant mistake that has now cost me a lot of time.  I instantiated both a UINavigationController and a custom TableViewController in my app delegate, but I didn't push my TableViewController onto the stack of views that are managed by my UINavigationController.  Instead, this is what I did:

- (void)applicationDidFinishLaunching:(UIApplication *)application {

    personListViewController = [[PersonListViewController alloc] initWithStyle:UITableViewStylePlain];
    personListViewController.view.frame = [UIScreen mainScreen].applicationFrame;    
    [window addSubview:personListViewController.view];
    
    navigationController = [[UINavigationController alloc] initWithRootViewController:personListViewController];
    [window addSubview:navigationController.view];

    [window makeKeyAndVisible];

}

Well, that is wrong.  Pragmatically, it seemed to work, except that by the time I started Presence 3 I discovered that in my TableViewController implementation, this was causing problems; not the least of which was that on initial load, viewWillAppear in my TableViewController's implementation was being called twice.  Since I was using viewWillAppear to call a method that would instantiate a NSInvocationOperation and add it to an NSOperationQueue, it was causing some problems.

Okay so lesson learned (twice).  In order to add a UITableViewController to a UINavigationView, you need to push the UITableViewController's view onto the stack of views managed by the UINavigationView.  Here's the correct code:

- (void)applicationDidFinishLaunching:(UIApplication *)application {

    navigationController = [[UINavigationController alloc] init];
    personListViewController = [[PersonListViewController alloc] initWithStyle:UITableViewStylePlain];
    [navigationController pushViewController:personListViewController animated:NO];
    [window addSubview:navigationController.view];
    
    [window makeKeyAndVisible];
}

A new Presence (1)

posted Nov 30, 2009 11:50 AM by Theresa McClain

So I didn't get a lot of coding work done over Thanksgiving; however, the turkey sure tasted great!

When I came back to my classwork for CS193P, I realized that I didn't feel quite right about the last assignment, and wanted a chance to rework it.  This would help me to make sure it was correct, and also remind myself of some things I might not have completely understood.  Like memory management.  I watched some specific sections of some of the first few lectures a 2nd time and it helped tremendously.

I really love having these courses available on iTunes.

Anyway, I reworked Presence 1 and I think the quality of the code is significantly improved.  It also looks nicer with a clean, themed interface.  It also saves some basic data; I think I actually met all the criteria for the extra credit work.

Playing with NSMutableArray and UITableViewController

posted Nov 27, 2009 3:03 AM by Theresa McClain

In lecture 8 of CS193P, Jason Beaver answered a question regarding a limit to the number of cells that a Table View can contain.  He briefly said that there is probably a limit, but it is high enough that he has never run into it.  Well, I decided to write up a very quick test to see if I could find out for myself.  I decided I would create a mutable array, and use a for/next loop to add string objects to the array, and then represent that array in a table view.  I could then increment the total number of elements in the array and find out if I can add too many.

I'll go ahead and post the code I used.  I started by creating a window-based application, and then creating a table view controller class, which I appropriately named "MyTableViewController."  Here's the header file:

#import <UIKit/UIKit.h>

@interface MyTableViewController : UITableViewController {
NSMutableArray *tableData;
}

@end

And here is the implementation file:

#import "MyTableViewController.h"

@implementation MyTableViewController

- (id)initWithStyle:(UITableViewStyle)style {
    self = [super initWithStyle:style];
    if (self) {
        tableData = [[NSMutableArray alloc] init];
for (int i=0; i<10000000; i++) {
            [tableData addObject:[NSString stringWithFormat:@"Number %d", i+1]];
        }
        NSLog(@"The array contains %d items.", [tableData count]);
    }
    return self;
}

- (void)dealloc {
    [tableData release];
    [super dealloc];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [tableData count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"DefaultCell"];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"DefaultCell"] autorelease];
    }
    cell.text = [tableData objectAtIndex:indexPath.row];
    return cell;
}

@end

As you can see it's really simple; I have one ivar, an NSMutableArray, tableData.  In initWithStyle, I allocate and initialize the array, and then add 10,000,000 strings to the array in this case.  After the array has been created, I write to the console the number of items in the array, as a simple way to indicate that the loop is completed.

Here is the delegate:

#import <UIKit/UIKit.h>

@class MyTableViewController;

@interface MyTableAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    MyTableViewController *myTableViewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

And the delegate's implementation file:

#import "MyTableAppDelegate.h"
#import "MyTableViewController.h"

@implementation MyTableAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    myTableViewController = [[MyTableViewController alloc] initWithStyle:UITableViewStylePlain];
    myTableViewController.view.frame = [UIScreen mainScreen].applicationFrame;
    
    [window addSubview:myTableViewController.view];
    // Override point for customization after application launch
    [window makeKeyAndVisible];
}


- (void)dealloc {
    [MyTableViewController release];
    [window release];
    [super dealloc];
}

@end

So that's it.  This is a really simple pattern for creating a quick and simple table view application showing some data.  Originally I had created this just to make sure I understood how to implement a simple table view, after watching lecture 8.

So naturally I wanted to test the limits, and see how many items I could add to the table view.

At first I started by adding 10,000 items to the tableData array, and I ran the app in the simulator.  It started quickly, and showed the table without any problems.

So I bumped up the number of items, adding 100,000 items to the tableData array.  Same thing, it started quickly and showed the table without any problems.

Again I bumped up the number of items, adding 1,000,000 items to the tableData array.  This time it took a little bit longer to load.  It took about 2 seconds before the console message appeared, indicating that the loop was finished loading items into the array.  Once that was completed; however, the table view had no problems loading.

Now it's worth pointing out that cells are more or less created dynamically; cells are instantiated (or if possible repurposed) as needed when the user scrolls.  The amount of time it takes to load is due to loading the array, not showing items in the table view.

So as my pattern might predict, this time I added 10,000,000 items to the tableData array.  This time it took just about 20 seconds to load, which seems to indicate a fairly linear increase in the amount of time it takes to load arrays (as you would probably expect).  Again, the table view loaded.

Well, I naively bumped the number of items I added to the tableData array to 1,000,000,000 before I realized it would probably take something like 33 minutes to load.  I'm not that patient.  I had to laugh to myself that I had even tried.

Well, there might be a faster way to load create huge arrays, but for the time being I'm going to believe Jason when he says that there is probably a limit, but it's big enough that it probably doesn't matter much.  :)

Presence 1

posted Nov 26, 2009 3:51 AM by Theresa McClain

I am having a blast learning iPhone/iPod Touch development!  Already, together with a friend, we've come up with 3 applications that we would like to write and submit to the app store.  We both need to work through CS193P first, although at least one of our ideas would make a great final project for the class.

I'm basically auditing CS193P, which is one of the courses available on iTunes U from Stanford University.  Having courses available over the web, and free of charge is amazing and extremely helpful.  This definitely speeds learning time and makes the process more interesting.  Also, since the lectures were filmed earlier in the year I can watch them at my own pace.  Some days I'll watch 2 or 3 lectures during the day, or watch the same lecture a couple of times just to make sure I really understand the material.

Okay so I've been working on the 4th assignment:  Presence 1.  This is actually a smaller part of a bigger, 4-part project where we'll be building a twitter client for the iPhone or iPod touch called "Presence."  Presence 1 is the first stage, and basically just sets up some basic navigation screens in the app.  The app opens showing a short list of people (2 total) consisting of an avatar image, a name, and a button labeled "View."  For Presence 1 we weren't supposed to use a table view; just place some controls onto the views and give them some static data.

When the user clicks on one of the "view" buttons, the view should change to show detail about that specific person, including their avatar image, name, and status.

So in general we needed to become familiar with the UINavigationController object, and use it with different views.  We needed to pass some basic information to the detail view.

As an extra credit portion of the assignment, it was suggested that we find a reason to create an additional view and save/retrieve some data for that view.  It seemed reasonable to add an "edit" screen, so that while viewing a person's detail, the user could click an "edit" button and change some information about that user.  (This is not something you would probably do in a real twitter client, but for the sake of learning iPhone development it seemed like an interesting thing to do.)

Well, I got a little carried away.  I created a new class called "Twit" which would describe people - basically, their twitter ID, name, location, Url, etc.  In the list view controller (PersonListViewController) I created an ivar array (NSArray *twitsArray) to hold an array of twits that would be displayed on the PersonListView view.

I know, this sounds confusing.

For the time being I decided *not* to use a mutable array; I would just stick to 2 people, or "twits" for Presence 1.  Besides, I was getting myself into trouble because I had intended to use NSUserDefaults to save the array so that my data would persist between app launches.

Well, I hooked it all up so that the user could view detail about a person, and edit data about that person.  Data was being saved, and passed along to the right places so that the user could go from the list view to the detail view, press the edit button and change the person's name and/or status, go back to the list view and see the name change.  They could then go into the detail view and see that the status was updated.  I won't go into too much detail because after reading what I've already written it just sounds pretty confusing.  I've been working on this for the last few hours, so it's all kind of in my head and it all makes sense to me, but I can't imagine someone reading this and making heads or tails of it.

Anyway, the last detail was to save my array of twits to NSUserDefaults so that the data would persist between app launches.  I found out that it is a little harder to do that than I had thought.  I had seen in the API reference, a way to retrieve an NSArray object from a key value - so I assumed it would be an easy task to save an array of (Twit) objects.

It turns out that it's easy to save an array of NSString objects, but not my own custom objects.  It can be done, but it's a lot more complicated than I had thought (plus, being 4:22am I'm just about ready to go to bed and start Presence 2 tomorrow).  Within the next lecture or two we'll learn how to retrieve that information from the Twitter API anyway, so all we'll have to save is the user's twitter ID.  NSUserDefaults was probably not meant to save complicated data structures like that anyway.  It seems a lot more suited for simple key/value pairs.

Okay so the point is that I had a lot of fun working on Presence 1, and am going to work on Presence 2 next (tomorrow).

Happy Turkey Day!

CS193P Issues & Questions

posted Nov 23, 2009 5:27 PM by Theresa McClain   [ updated Nov 24, 2009 3:24 AM ]

Here are a few questions I have with regards to Assignment 3 for CS193P.  I'm putting these questions here in order to document them, and hopefully come back and answer the questions as I find the answers.  If you happen to come across this site, you will need to understand the assignment requirements in order to know what these questions relate to.

Question 1:

In Controller.m I'm using the method awakeFromNib to obtain user defaults and set up the polygon in order to draw it:

- (void)awakeFromNib {
NSLog(@"I am in theawakeFromNib method.");

NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
NSInteger numberOfSides = [userPreferences integerForKey:@"numberOfSides"];
polygonShape.useDashedLines = [userPreferences boolForKey:@"useDashedLines"];
// QUESTION:  When I release userPreferences, the program fails to load the
// defaults.  When I don't release userPreferences, it seems to work
// correctly.  Why can't I release userPreferences?
if (numberOfSides > 2) {
polygonShape.numberOfSides = numberOfSides;
} else {
polygonShape.numberOfSides = numberOfSidesLabel.text.integerValue;
}
[self updateInterface];

}

As you can see I added my question in comments; I believe I need to release the userPreferences object; however, when I do the application crashes.


ANSWER:  I should have paid more attention to the memory management section of previous lectures.  During lecture 6, I was reminded that you only need to call release when you either "alloc", "copy", or "retain" an object.  These all increment the retain count for the object, and release decrements the retain count.

I believe this also answers question #2 below as well.

Question 2:

This one is going to be easier to answer.  I override the dealloc method in Controller.m, in order to do some cleanup work:

- (void)dealloc {
NSLog(@"Dealloc is being called from within Controller.");

// QUESTION:  I believe I heard during one of the lectures that you don't
// have to release view objects that are placed on the window.  If this is
// the case, most of these don't need to be released.
    [decreaseButton release];
decreaseButton = nil;
    [increaseButton release];
    increaseButton = nil;
[numberOfSidesLabel release];
    numberOfSidesLabel = nil;
[polygonName release];
    polygonName = nil;
[polygonShape release];
    polygonShape = nil;
[polygonView release];
    polygonView = nil;
[sidesSlider release];
    sidesSlider = nil;
[lineSegementedControl release];
lineSegementedControl = nil;
[super dealloc];
}

There are a number of objects that I'm using that are represented on the window like increaseButton and decreaseButton.  I think I heard in one of the lectures that you don't need to release these, but I'm not 100% confident.  To be safe I went ahead and released them.

iPhone Application Development

posted Nov 23, 2009 5:20 PM by Theresa McClain

This is interesting; after switching from iPhone to Android, I have started a new mobile development project with a friend which requires developing against the iPhone.  Since I haven't done any iPhone development yet, and even more important, I haven't done any Objective-C development yet, I'm starting by taking an online course for iPhone development.  The course is available through iTunes U, called "iPhone Application Programming, from Stanford University.

I've watched the first 5 lectures so far, and have done the first few assignments.  I am so excited about learning something new, and working towards developing an application.

The application we're building has to do with poker - I don't want to say too much because it's a fairly unique idea that hasn't yet been done, at least on either the iPhone app store or the Android store.  If we make this work, it will be the first, and I think it would be a valuable tool for any serious poker player.

I'm considering switching to an Android phone...

posted Oct 13, 2009 7:31 PM by Theresa McClain

I've owned and enjoyed each iPhone that has come out, except for the latest, iPhone 3GS.  I haven't found a compelling reason to upgrade yet, and in the meantime I find myself considering moving to an Android phone for a few reasons:

  • There is now an Android phone available on Sprint, which is my favorite network because of their data network.  AT&T has been, well, quite a disappointment, and I've always said that if the iPhone were available on the Sprint network I would have stayed there.
  • I'm tired of the hassle of having to jailbreak my phone to get the applications I want.  Android, being open source, doesn't seem to have those problems.  I've lost data and hours fiddling with my phone to jailbreak it, and I'm done fiddling.  I just want it to work, and I want the right to use the applications I want to use.
  • I believe that Apple created a device that was revolutionary; however, the revolution is over.  Other operating systems are coming up to speed and innovating in their own unique ways.  I like the iPhone, but it is no longer the only game in town.  I'm not saying that it isn't a great device with great software; it's just that there are now compelling alternatives.
  • As you can tell from this site, I use Google Apps extensively, and Android seems to integrate better with Google services. (duh!)

Connecting a Novatel Wireless MiFi2200 to an Airport Extreme

posted Oct 6, 2009 2:05 AM by Theresa McClain

I am so happy right now; I've spent more hours pulling my hair out trying to get this to work than I care to admit.  But finally I can say that I have completed the task, and feel like sharing my accomplishment with the world.

I had purchased a Novatel Wireless MiFi2200 on the Sprint network.  This device has worked great for me on the road, where I can connect to it over wifi with my netbook and get Internet access.  The only big limitation here is that you can only connect 5 devices or computers to the device at a time.  (As an aside, I actually prefer to connect to the Mifi2200 with my iPhone, in order to use Sprint's faster network service.  AT&T's network is, well, an embarrassment to iPhone users...)

I also have a network at home, without Internet access.  I had been using an Airport Extreme with several Airport Express base stations to play music in various rooms throughout my home.  It seemed like I should be able to connect my Airport Extreme to my MiFi2200 device, in such a way that I can have Internet access on my home network, and if done right, I should be able to use NAT on my Airport Extreme, thereby allowing me to connect more than 5 devices to the Internet.

One word of caution:  my Sprint account only allows 5GB of data each month, so I'm cautious not to exceed that as I connect more devices to the network.

I have no idea what I'm doing here; I'm not a network person, but I can be doggedly determined to find a solution to a problem that seems like it should be solvable.  This might not be the only or best way to solve this problem, but it works.

The first step was that I purchased a wireless bridge in order to turn the wifi signal from the MiFi2200 into an ethernet connection.  That was simple enough; I bought a D-Link Xtreme N Duo wireless bridge/access point and within minutes had it set up.  As a bridge, it shows up as one of the devices on my MiFi2200, and provides anything connected to it via ethernet with the ability to obtain an IP address from the DHCP server in the MiFi2200.  The configuration of the bridge was pretty standard; I had to connect a laptop to the bridge with an ethernet cable, and open a browser to http://192.168.0.1 to get to the device's configuration pages.  It asked me for the SSID and password for my MiFi2200 network and it connected up perfectly.

Now the Airport Extreme can act as a bridge too; however, I'm limited to 5 IP addresses from my MiFi2200, which is not enough for all the devices on my internal network.  I needed my Airport Extreme to be a router with NAT, in order to share 1 IP address from my MiFi2200 with all the devices on my internal network.

So far there's no mystery; all I've basically done was to get the wireless network to be available on an ethernet cable.

Apple usually makes things so simple to set up and configure; however, in this case the Airport Extreme seemed incredibly difficult to get right, and it all had to do with a simple "error" message that the Airport Extreme displayed when I tried to configure it with what seemed like the correct settings.  I think I reset the device to factory settings and reconfigured it several times; each time I created a new wireless network and gave it a password.  I set it up to share a single IP address with NAT, and saved the settings.  I configured the Airport Extreme to obtain an external IP address using DHCP, and to use DHCP itself to distribute IP addresses internally.

So to clarify, my MiFi2200 is itself a router with DHCP that provides IP addresses for up to 5 devices in the range 192.168.1.2 - 192.168.1.10.  I expected my Airport Extreme to obtain one of those IP addresses.

I intended for my Airport Extreme to be a DHCP server for all the devices on my internal network, and share one of the 192.168.1.X addresses it obtained from my MiFi2200 device, assigning internal addresses in the range 10.0.0.1 - 10.0.0.200.

This all seems simple enough.

Each time the Airport Extreme rebooted and reported problems with the device.  I don't have the exact text, but it kept saying that it was having problems with two NAT devices on the network.  I had the option of ignoring the problem, or setting the device to bridge mode (which was the recommended solution).  When I finally, bravely, chose to ignore the problem and rebooted the device again, the green light came on, and when the Airport Utility software found the Airport Extreme device again it had no problems to report.

Even better, all the devices in my internal network have 10.0.0.x addresses, and - even more important - Internet access!  The configuration pages on my MiFi2200 device show 2 IP addresses assigned - one to my wireless bridge, and another to the Airport Extreme.  All of my Airport Express base stations work just like they did before (I like to have music in my bathroom).  :)

I'm sure there must be security considerations here, so I'm going to do some additional investigation to find out if there are potential problems with having what is basically two routers connected together like this.  For now however, I'm happy with my network.

Theresa

1-10 of 11