Latest Publications

First Impressions with Objective-C

I’ve started to learn Objective-C, the programming language for Macs and the iPhone. So far I really like it. The philosophy and syntax around it are quite different from anything I’m used to. In most Object Oriented Languages, You have very strict classes with inheritance and methods; properties for accessing those classes. And in most OOLs (Object Oriented Languages) you have something called generics so that you don’t have to keep creating new classes because of a type difference. (Types are like numbers vs strings BTW.) If I have a list of integers (numbers) then I don’t have to make a second list just so I can have a list of strings. In fact a lot of time and effort goes into this sort of thing. (There is also the fragile base class problem.) This is where Objective-C really shines.

Objective-C is C with SmallTalk features added. The biggest difference is how object methods are called. In C# (or C++ or Java or whatever) if you want to call a method you use the ‘.’ dot operator (or ‘->’ arrow operator in C++.) For Example, pretend ‘Cat’ is an object with a single method of ‘Speak’. In C# you would write ‘Cat.Speak();’ to call the Speak method of the Cat object. In Objective-C however you don’t call methods directly. You pass messages to objects. So in our example we would write ‘[Cat Speak]‘ in Objective-C. On the surface this looks like a small syntax change. Instead of a ‘.’ operator you place the items in ‘[]‘ brackets with a space. But what these two items actually do is the key.

In C# the Cat method must have a Speak method or else it will throw an error. And that Speak method’s signature must match exactly. (signature is the Return Type and all the parameters [in order] on the method.) In Objective-C on the other hand, the Cat object doesn’t need to have a Speak method. If it doesn’t, the call will simply return false. No errors will be thrown. (Warnings will be thrown since we did specify a type.)

“But Chris!”, you say, “That’s one of the strengths of C#!” (I don’t know why you use so many exclamation marks, I can hear you, I promise.) And you’re right, type checking is a strong feature of those languages. You could tell Objective-C to throw errors in these situations. But let me explain why this is such a good thing.

If you’ve ever programmed a larger application, then you will be familiar with the concept of boxing and/or generics. (I’m going to stick with C# examples.) Now let’s say you write an application for a pet hospital. You need to keep a list of all the pets in the system. Let’s also assume each type of pet has it’s own class. (Cat, Dog, Snake, Hamster, Birds, Fish, pygmy goat, etc.) You have two choices: You could use boxing to add them all to an ArrayList of objects (because all classes inherit from object). Or you could use Generics to create a list for each type of Animal. Both of these methods are reasonable but have problems. They both require you to know the type of the object at compile time (or be able to get the type so you can cast it.) The reason is, not all of the animals will have the same methods. Not all of them can ‘Speak’ or ‘Fly’ so if you need to call those methods, you need to know the type at compile time or else an error will be thrown. Now there are creative ways to get around this problem. Which is why I said a lot of code and time is spent trying to get around this issue in the first paragraph.

But this really isn’t a problem in Objective-C. You can store all the animals in a single list, and not worry about if they implement ‘Speak’ or ‘Fly’. You can call the methods on ALL of the objects. Only the ones that actually implement the method will do something. All the time and energy to get around this problem in other languages is simply a non-issue in Objective-C. This isn’t Objective-C’s only strength, just one that I thought was most relevant to my daily programming.

There is one tremendous problem with Objective-C. The base classes (called Foundation) are Mac only. GNUStep tries to implement Foundation and some others on Windows and Linux but the downside is to great. Window users would have to install GNUStep (which isn’t an easy process and involves MinGW) in order to use anything in Objective-C. Until Foundation can either be statically built into the application, or a simple (like a DLL) way to include it is developed, I’m afraid Objective-C is going to stay Mac only. Not only that, but in limited use on the Mac (you can use C++ on the Mac).

Apple pushes Objective-C but that just isn’t enough in my opinion. Someone (preferably Apple) needs to make at least the core classes available for the three major operating systems (Windows, OSX, Linux). If there where to happen, then I think Objective-C could gain a lot of traction. Over time it could gain more support than C# or Java, or maybe even C++. But for now that is just my fantasy. I can use Objective-C for iPhone development and OSX development. That will have to be enough for now. I’ll just stick to C# for my windows development.

  • Share/Bookmark

Objective-C

I switched over to Apple in 2003/2004 ish, and I’ve never really done any programming on it. Because of the iPhone, my intrest in apple development has peaked. So these last few days I’ve been looking into it and building some sample apps in XCode.

I’m intrested in building a Visual Novel of sorts. As a kid I loved choose your own adventure books and I’ve played a few VN that I liked. I’ve also played a bunch that I hated.

But being the writer & programmer that I am. I thought it could be fun to build my own.

There are a number of ways I could build this, the easyest being HTML and JavaScript (another AIR app). But I kinds want this to be on the iPhone (partly becuase if it successeds I could sell it).

So now I’m looking at how it can be done in objective-c and cocoa. To be honest I haven’t programmed in C (or C++) in over 7 years. Looking over samples rings some bells, but Cocoa is a new beast all together. Anyway, I think I’m going to try this objective-c programming.

I know this isn’t very zen of me. I could have something up and running in a matter of hours if I stuck with JavaScript or C#. I don’t even have a story or graphics yet. I just have a vage idea of a zombie survival story.

I really should write the story first, then worry about programming.

Still, I find myself here even when I know I shouldn’t.

  • Share/Bookmark

Difficulties

I’ve been having problems completing this next step. I think I’ve been trying to solve two difficult problems at once. The Dynamic loading of the property, and the joining of the two tables.

So I’m taking a step back. Let’s solve the join first. How can I solve this problem with the code I already have?
Well I could just manually make the call to Tag in the User property. I would need to use the .Where() method to get what I want.
But, the .Where() method only looks at the objects table. I need to reference a joining table (tag_user to be exact.)

So, I need to be able to specify a joining table in the .Where() method, and then pull normally. I need to identify the table name, the matching columns, and then do the .Where() method normally.

Sounds good, I’ll get on this in the morning.

  • Share/Bookmark

How

I changed some things around. I added a GetProperty and SetPropery because I didn’t like have to do a check all the time. I think it simplifies things and allows _properties to be private instead of protected.

I also created a GetColumns that gives the full name with an optional prefix for select statements. I created a GetTable to help out with this as well.

  • Share/Bookmark

helpers

I which I could put this in the base class somehow, but we can simplify the code if the inherited class override the By and Where methods like so

        ///
        /// Get a single User By the property and value specified
        ///
        ///
        ///
        ///
        static public DBUser By(string property, object value)
        {
            return (DBUser)DBObject.By(property, value, typeof(DBUser), DBUser._conn);
        }

        ///
        /// Returns DBList where all the DBUsers have a matching property
        ///
        ///
        ///
        ///
        ///
        static public DBList Where(string property, string evaluator, object value)
        {
            return DBObject.Where(property, evaluator, value, typeof(DBUser), DBUser._conn);
        }

Then we can make simple calls like:

DBUser u3 = DBUser.By("firstname", "chris");
//and
DBList users1 = DBUser.Where("id", "<=", 1313);
  • Share/Bookmark

Next Step

Well I guess the next thing to do is to allow an object to relate to another object. For work I use tags for objects (as I discussed earlier) so that’s going to be my starting point.

So I have two objects. User and Tag.

A single User record can have an unlimited number of tags.
A single tag record can have only one user. But a Tag object can have an unlimited number of users.

So we have a many to many relationship, with a table joining them. The table already exists (tag_user) so I need to specify that.

When I get a user, it should get all of his tags as well, like wise when getting a tag it should get all the users. except that might not always be a good idea. What if I have 10,000 users with the tag ‘Public’ I might want to get that tag without getting all the users who have that tag. alsoIf i’m getting a list of users and they all have the ‘Public’ tag, it shouldn’t the tag shouldn’t pull that list of 10,000 for each user in my list. It should only pull it once, and thats only if I ever request it.

So I need to do some lazy loading. Say it only gets the related object if it’s requested. So if I get a list of users, it’ll just fill out that list. If I access the tags on one of the users, then it’ll go and fetch his tags.

So first things first:

Create Many to Many relationship, if you access the related object, then it goes and gets it.

  • Share/Bookmark

DBObject.Where

Ok it works. I also created a checker for the opertators so we don’t get invalid/malicious code.

 ///
        /// Returns a DBList of objects that match the Where
        ///
        ///
Property to Match        ///
"=", "<", ">", "LIKE"        ///
Value to Match        ///
Type of the object to get (Must inherent from DBOBject)        ///
Connection String to use        ///
        static protected DBList Where(string property, string evaluator, object value, Type who_type, string connection)
        {
            DBList list = new DBList();

            //Find the Column Name and check that we have everything, if fail return the blank list
            string column_name = DBObject.GetColumn(property, who_type);
            if (column_name == string.Empty || evaluator == string.Empty || value == null) { return list; }
            //Check the evaluator, If fail, Return a blank list
            evaluator = DBObject.CheckEvaluator(evaluator);
            if (evaluator == string.Empty) { return list; }

            //Create the WHERE
            string query = DBObject.SelectQuery(who_type) + " WHERE " + column_name + " " + evaluator + "?value";

            using (MySqlConnection conn = new MySqlConnection(connection))
            {
                MySqlCommand cmd = new MySqlCommand(query, conn);
                cmd.Parameters.AddWithValue("?value", value);

                try
                {
                    conn.Open();
                    MySqlDataReader reader = cmd.ExecuteReader();

                    while (reader.Read())
                    {
                        DBObject obj = (DBObject)DBObject.FromReader(reader, who_type);
                        list.Add( obj );
                    }

                    conn.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("nnERROR:n" + ex.Message);
                }
            }

            //No matter what happens, always return a valid list
            return list;
        }

///
        /// Accepted Evaluators: '<', '<=', '>', '>=', '=', '!=', 'LIKE', 'REGEXP', 'IS NOT'
        ///
        ///
        ///
        static protected string CheckEvaluator(string evaluator)
        {
            //Lower and trim
            evaluator = evaluator.ToLower().Trim();

            switch (evaluator)
            {
                case "<":
                    return evaluator;
                    break;
                case "<=":
                    return evaluator;
                    break;
                case ">":
                    return evaluator;
                    break;
                case ">=":
                    return evaluator;
                    break;
                case "=":
                    return evaluator;
                    break;
                case "!=":
                    return evaluator;
                    break;
                case "like":
                    return evaluator;
                    break;
                case "regexp":
                    return evaluator;
                    break;
                case "is not":
                    return evaluator;
                    break;
                default:
                    return string.Empty;
            }
        }

And an example:

//Now lets try to get a list of users
            Console.WriteLine("nnTrying the Where Methiod");
            DBList users1 = DBObject.Where("id", "<=", 1313, typeof(DBUser), Utility.connMy);
            foreach (DBUser u4 in users1)
            {
                Console.WriteLine(u4.ID + " : " + u4.FullName);
            }
  • Share/Bookmark

Next step.

I have two thoughts of what the next step should be. The first is the ability to return a collection of objects (maybe LINQ computable, but I’m not worried about that right now.) The other is that the object should be able to fetch it’s own tags.

In my system, each object has tags associated with it. This allows objects to ‘subscribe’ to other objects. For example, we have a user object and a task object. The user can use tags to subscribe to a task (or many tasks). Many users can subscribe to a same task using this method. The tasks also have an optional prefix. This prefix can say things like; who owns the task, who can modify it, who can complete it, etc. (Just having the tag means you’ve subscribed and can thus view it.)
So when I get the object it really needs to go and fetch all if it’s tags as well.

Because objects rarely have only a single tag, I should probably start with the collections. I’m going to call them lists from now on. These lists must inherit from CollectionBase class in order keep compatibility.

So I think what I want, is a DBList : CollectionBase object that can be used as the return value for static methods.

So I could do something like 

(DBList)DBObject.Where(“Age”, “>=”, “25″, typeof(DBUser), Utility.connMy);
and it will return a list of all the users whos age is 25 or older. Or
(DBList)DBObject.Where(“Nake”, “LIKE”, “%chris%”, typeof(DBUser), Utility.connMy);
I’m not opposed at all of passing the evaluator as text (“>=”, “LIKE”, “=”, “<", etc.) Where this does become a problem is when we need to specify more than a single Where clause. But I figure thats a bridge we can cross when we come to it.
  • Share/Bookmark

DBObject rev .03

Alright, now I have a working .By

It actually simplifies things a lot, to the point I’m starting to double think making everything static.

As you can see, you can simply call something like:

    DBUser chris = (DBUser)DBObject.By("firstname", "chris", typeof(DBUser), Utility.connMy);
    DBUser bob = (DBUser)DBObject.By("id", "1327", typeof(DBUser), Utility.connMy);

And get back a valid user.

This isn’t prefect, for example, what if there are two people named Chris? this would only return an object for the first record returned. Which leads into my next step, returning a list of DBObjects.

    ///
    /// This is the baseclass for DB Object created by Chris Richards
    ///
    [Serializable]
    public class DBObject
    {
        //Holds all the properties in the object
        protected Hashtable _properties = new Hashtable();

        protected DBObject() { }

        ///
        /// Get the Object By a single property
        ///
        ///
The Property you want to get the Object By        ///
Value of the Property        ///
Type of the object to get (Must inherent from DBOBject)        ///
Connection String to use        ///
        static protected DBObject By(string property, object value, Type who_type, string connection)
        {
            DBObject obj = null;    //Either it will be set to a valid object, or it will remain null

            //Find the Column Name for the property requested
            string column_name = DBObject.GetColumn(property, who_type);

            if (column_name != string.Empty)
            {
                //Append the WHERE clause
                string query = DBObject.SelectQuery(who_type) + " WHERE " + column_name + "=?value";

                using (MySqlConnection conn = new MySqlConnection(connection))
                {
                    MySqlCommand cmd = new MySqlCommand(query, conn);
                    cmd.Parameters.AddWithValue("?value", value);

                    try
                    {
                        conn.Open();
                        MySqlDataReader reader = cmd.ExecuteReader();

                        if (reader.Read())
                        {
                            obj = (DBObject)DBObject.FromReader(reader, who_type);
                        }

                        conn.Close();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("nnERROR:n" + ex.Message);
                    }
                }

            }

            //Return what we have
            return obj;
        }

        ///
        /// Gets the Column name of a Property
        ///
        ///
Name of the Property        ///
Type of the object to get (Must inherent from DBOBject)        ///
        static protected string GetColumn(string property, Type who_type)
        {
            foreach (PropertyInfo who_properity in who_type.GetProperties())
            {
                //Find the matching property
                if (who_properity.Name.ToLower() == property.ToLower())
                {
                    foreach (object attribute in who_properity.GetCustomAttributes(false))
                    {
                        //Get the column
                        if (attribute is DBColumn)
                        {
                            return ((DBColumn)attribute).Column;
                        }
                    }
                }
            }

            //Couldn't find it
            return string.Empty;
        }

        ///
        /// Gets the SELECT ... FROM query for this object
        ///
        ///
Type of the object to get (Must inherent from DBOBject)        ///
        static protected string SelectQuery(Type who_type)
        {
            //Generate the Select Query
            string _select = "SELECT";

            //Now Set all the select columns
            foreach (PropertyInfo properity in who_type.GetProperties())
            {
                foreach (object attribute in properity.GetCustomAttributes(false))
                {
                    if (attribute is DBColumn)
                    {
                        _select += " " + ((DBColumn)attribute).Column + ",";
                    }
                }
            }

            //Remove the last Comma
            _select = _select.Substring(0, _select.Length - 1);

            //Get the Table Name for this Object
            foreach (object attr in who_type.GetCustomAttributes(false))
            {
                if (attr is DBTable)
                {
                    //Add the Table to our List
                    _select += " FROM " + ((DBTable)attr).Table;
                }
            }
            //Now Return it
            return _select;
        }

        ///
        /// This will populate the object from the reader.
        /// It will not advance the reader.
        ///
        ///
        ///
Type of the object to get (Must inherent from DBOBject)        ///
        static protected DBObject FromReader(MySqlDataReader reader, Type who_type)
        {
            DBObject who = (DBObject)Activator.CreateInstance(who_type, true);
            //Look for all the properities
            foreach (PropertyInfo properity in who_type.GetProperties())
            {
                if (properity.CanWrite)
                {
                    object[] attributes = properity.GetCustomAttributes(false);
                    foreach (object attribute in attributes)
                    {
                        if (attribute is DBColumn)
                        {
                            try
                            {
                                properity.SetValue(who, reader[((DBColumn)attribute).Column], null);
                            }
                            catch (IndexOutOfRangeException)
                            {
                                //Just Skip it
                            }
                            catch (Exception ex)
                            {
                                string junk = ex.Message;
                            }
                        }
                    }
                }
            }
            return who;
        }
    }

Here is my test object.

    [Serializable]
    [DB.MetaData.DBTable("users")]
    class DBUser : DBObject
    {
        [DBColumn("first_name")]
        public string FirstName
        {
            get
            {
                if (!_properties.ContainsKey("first_name"))
                {
                    _properties["first_name"] = string.Empty;
                }
                return (string)_properties["first_name"];
            }

            set
            {
                _properties["first_name"] = value;
            }
        }

        [DBColumn("last_name")]
        public string LastName
        {
            get
            {
                if (!_properties.ContainsKey("last_name"))
                {
                    _properties["last_name"] = string.Empty;
                }
                return (string)_properties["last_name"];
            }

            set
            {
                _properties["last_name"] = value;
            }
        }

        public string FullName
        {
            get { return this.FirstName + " " + this.LastName; }
        }

        [DBColumn("id", true)]
        public int ID
        {
            get
            {
                if (!_properties.ContainsKey("id"))
                {
                    _properties["id"] = -1;
                }
                return (int)_properties["id"];
            }

            set
            {
                _properties["id"] = value;
            }
        }

        protected DBUser() { }

        static public void DEBUG()
        {

            string suery = DBObject.SelectQuery(typeof(DBUser)) + " WHERE id=?id";

            Console.Write("Going to run: " + suery);

            using (MySqlConnection conn = new MySqlConnection(Utility.connMy))
            {
                MySqlCommand cmd = new MySqlCommand(suery, conn);
                cmd.Parameters.AddWithValue("?id", 1327);

                try
                {
                    conn.Open();
                    MySqlDataReader reader = cmd.ExecuteReader();

                    if (reader.Read())
                    {
                        DBUser user = (DBUser)DBObject.FromReader(reader, typeof(DBUser));
                        Console.WriteLine("nGot: " + user.FullName);
                    }

                    conn.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("nnERROR:n" + ex.Message);
                }
            }

            //Now try the properity
            Console.WriteLine("nnTrying the By Methiod (ID)");
            DBUser u2 = (DBUser)DBObject.By("id", "1327", typeof(DBUser), Utility.connMy);
            if (u2 != null)
            {
                Console.WriteLine("Got: " + u2.FullName);
            }
            else
            {
                Console.WriteLine("Oops, got a null back");
            }

            //Try another property
            Console.WriteLine("nnTrying the By Methiod (FirstName)");
            DBUser u3 = (DBUser)DBObject.By("firstname", "chris", typeof(DBUser), Utility.connMy);
            if (u3 != null)
            {
                Console.WriteLine("Got: " + u3.FullName);
            }
            else
            {
                Console.WriteLine("Oops, got a null back");
            }
        }
    }
  • Share/Bookmark

Next Steps

So far I think things are going good. I could even start to use this in the real world. But I’d like to complete a few more features first.

Having a .By(Property, Value) method. That way we could get an object by any defined property.

Having something like .Get(Property) and .Set(Property) that will return the value of any property. Right now the child class has to access the underlying _properties hash. I don’t necessarily like that idea, it gives the developer a chance to screw up more than just the property they are working with (since all properties are through the same hash.) Also methods like that would mean that the developer wouldn’t have to check if the property already exists and if they should return a default value. On the down side, how do you define what the default should be? probably in the attribute.

  • Share/Bookmark