Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Welcome to my series on coming to grips with the awesome language that is Objective-C. Throughout this small series of articles, my aim is to take you from no prior experience with Objective-C to using it confidently in your own applications. This isnt a rush job so dont expect to just skim through the basics and be away well be going through not just the bare essentials, but also the best practices you can apply to ensure your code is the best it can be. Lets jump straight in!
What is Objective-C?
If youre reading this series then Ill hazard a guess that you already know, but for those of you who dont, dont worry as by the end of this part youll know what it is back-to-front and inside-out. Objective-C is an object oriented language which lies on top of the C language (but I bet you guessed that part!). Its primary use in modern computing is on Mac OS X as a desktop language and also on iPhone OS (or as it is now called: iOS). It was originally the main language for NeXTSTEP OS, also known as the operating system Apple bought and descended Mac OS X from, which explains why its primary home today lies on Apples operating systems. Because Objective-C is a strict superset of C, we are free to use C in an Objective-C file and it will compile fine. Because any compiler of Objective-C will also compile any straight C code passed into it, we have all the power of C along with the power of objects provided by Objective-C. If youre a little confused at this point, think of it this way: everything C can do, Objective-C can do too, but not the other way around.
Youve probably already guessed how it works: inputfile.m contains our code (.m is the extension used for Objective-C files) and -o tells gcc we want our executable to be called whatever we specify next, which in the example above is outputfile. To run our creation after compiling, we simply type: 1. ./outputfile Simple. When you compile, the compiler will generate any errors, notifications or warnings related to the syntax of your code. Errors generated when compiling are understandably referred to as compile-time errors, and this is often the most stressful part of writing an application (especially when your code isnt compiling because you put a single character in the wrong place or forgot to end a line with a semi-colon). Compiling can also take time when youre writing large applications consisting of multiple files, which is also another reason why compiling can be a tedious experience. This fact has led to a ubiquitous programmer joke often seen on the t-shirts of men with unkempt beards: Im not slacking off. My code is compiling.
The Basics
Objective-C itself isnt that hard to learn. Once you get to grips with the basic principles, you can pick the rest up as you go along pretty easily. You do need to have an understanding of the fundamentals of C programming though, and that is what the rest of this tutorial will cover. Lets look at a basic application in C:
1. #include <stdio.h> 2. int main(){ 3. printf("Hello World\n"); 4. return 0; 5. } All this application will do when you run it is display the string Hello World in Terminal and exit. NOTE: Curious about the return 0 statement? Because we told the compiler that the function main() will return an integer, we return the constant integer value 0 at the end of the function. By convention, returning 0 signals to the calling program that our program finished execution without any errors. To try this for yourself, fire up Xcode and make a new Objective-C class. Delete all the code Xcode gives you by default and stick the above code in. Once youve done that, you can compile it using Terminal. Open Terminal and change to the location where your file is, if you saved to the desktop then simply type cd desktop so that Terminal is now reading from your Desktop. Then type this command:
1. gcc program1.m -o program1 Your program should compile with no errors. To run it, simply type: 1. ./program1 Then hit return. Awesome, so what actually happened there? Well, first we imported a library called stdio which manages the standard i/o (input output) functions, like printf(). We then create a function called main which should return an int or integer which is basically a number with no decimal point. We then use the printf() function to output Hello World in to terminal. The \n we use tells Terminal to put a newline after the text. Finally, we return 0 (remember we said main should return an integer) which tells the operating system everything went fine. We use the name main because this is triggered automatically when the program is executed. So far everything should be pretty simple: we wanted to write some text to Terminal, so we imported a library with a function for writing text, then we used a function from that library to write the text. Imagine that what you import is a physical library and printf() is one of the books available.
Variables
Soldiering ahead, were now on to variables. One of the fundamental things we need to be able to do in our applications is temporarily store data. We do this using variables, which are containers that can hold various types of data and be manipulated in various ways. We use variables to store all sorts of data, but we must first tell the compiler what were going to store in it. Here are several of the most important variables that you should know about for now:
int for storing integers (numbers with no decimal point) char for storing a character float for storing numbers with decimal points double same as a float but double the accuracy
When were not using variables, were often using constants. A constant will never change: we always know what the value will be. If we combine constants we get a constant expression, which we will always know the result of. For example:
1. 123 + 2 = 125 This is a constant expression, 123 + 2 will always equal 125, no matter what. If we substituted a constant for a variable, the new expression would look like this: 1. 123 + i = ?
Because i is a dynamic variable, we do not definitely know the result of this equation. We can change i to whatever we want and get a different result. This should give you an idea of how variables work. One thing we still need to know is how do we display variables like we displayed Hello World above? We still use the printf() function, except it changes a little this time: 1. #include <stdio.h> 2. int main(){ 3. int someNumber = 123; 4. printf("My number is %i \n", someNumber); 5. return 0; 6. } What weve done here is told the function printf() where we want our integer to appear, then where it can be found. This is different to a lot of languages such as PHP where you could just place the variable in the text. We are not just limited to just one variable in printf(). The function can accept multiple parameters separated by commas, so we can pass in as many as we have formatting signs for in the text. Above we use %i as a formatting sign because we were including an integer. Other variables have their own format specifiers:
One thing I want to touch on before we move on is the char type. A variable of type char can only handle single characters, when thats all we need this is great, but if we need a string of text its pretty useless. To get round this, we use something called a character array. Imagine you have a sentence that is 11 characters long (like Hello World dont forget to include the space), a character array is like having 11 chars but all glued together. This means that the value of the character array overall is Hello World but char[0] is H. In brackets is the char youre after, because we put 0 we get the first character. Dont forget that counting in arrays usually starts from 0, not 1.
Conditionals
When an application needs to make a decision, we use a conditional. Without conditionals, every time you ran your application it would be exactly the same, like watching a movie. By making decisions based on variables, input or anything else, we can make the application change this could be as simple as a user entering a serial number or pressing a button more than 10 times. There are a few different types of conditionals, but for now were just going to look at the most common and basic: the if statement. An if statement does what it sounds like, it checks to see if something is true, then acts either way. For example:
1. 2. 3. 4. 5. 6. 7. 8. 9.
#include <stdio.h> int main() { if(1 == 1) { // This is always true // Do some stuff here } return 0; }
If 1 is equal to 1, then whatever is between the brackets is executed. You might also be wondering why we used two equals signs instead of one. Using two equal signs is an equality operator, which checks to see if the two are equal to each other. If we use a single equal sign then were trying to assign the first value to the second value. Above, since 1 will always be the same as 1, whatever is in the brackets would be executed. What if we wanted to do something if this wasnt true though? Thats where else comes in. By using else we can run code when the if conditional returns false, like so: 1. int main(){ 2. if(1==1){ 3. // Do some stuff here. 4. } 5. else{ 6. // The universe is broken! 7. } 8. 9. return 0; 10. } Of course, in real life, we wouldnt be checking to make sure 1 is the same as 1, but the point is made. Consider an application that closes if you press the close button three times (annoying but relevant). You could check in the brackets to see how many times it has been pushed. If it is lower than 3, then your else block could execute code to tell the user how many more times the button must be pushed to exit. Well look at conditionals more when we come to use them in our applications further along in the series.
Loops
Now lets investigate a programming loop. Loops, as the name suggests, let us loop through a piece of code and execute it multiple times. This can come in very handy in situations such as populating a list or repeating a piece of code until a conditional returns true. There are three types of loops, in order of most common: for, while, and do. Each one is used to repeat execution of a block of code, but they function differently. Here are examples of each:
1. // if loop 2. int main () { 3. int i = 9; 4. int x = 0; 5. 6. for (x = 0; x < i; x++){ 7. printf("Count is: %i\n", x); 8. } 9. 10. return 0; 11. } This may look a little complex at first, but it really isnt. In the parentheses after for is the initiator, a conditional, and the action. When the for loop starts it executes the initiator, which in our case above sets x to 0. Each time the loop runs (including the very first time) it checks the conditional, which is "is x smaller than i?" Finally, after each loop through the code, the loop runs the action - which above increments x by one. Simple. Since x is increasing by one each time, x will soon no longer be less than i and the loop will finish and the program will carry on running. 1. // while loop 2. int main () { 3. int x = 0; 4. while (x < 10){ 5. printf("Count is: %i\n", x); //Watch OUT! Something is missing. 6. } 7. 8. return 0; 9. } Similar to the for loop, the while loop will execute the code between the brackets until the conditional is false. Since x is 0 and we don't change it in the code block, the above would run forever, creating an "infinite loop." If you wish to increment x, then in the case of our while loop you would do this between the brackets: 1. // while loop 2. int main () { 3. int x = 0; 4. while (x < 10){ 5. x++; 6. printf("Count is: %i\n", x); 7. } 8. 9. return 0; 10. } The do loop is essentially the while loop, except the conditional runs after the block of code. What this means is when using a do loop, the code is guaranteed to run at least once:
1. // do loop 2. int main () { 3. int x = 0; 4. do { 5. x++; 6. printf("Count is: %i\n", x); 7. } while(x < 10); 8. 9. return 0; 10. }
Pointers
Pointers can cause a lot of confusion with newcomers to programming or just newcomers to C. It is also not immediately clear to some people how they are useful, but youll gradually learn this over time. So, what is a pointer? As the name implies, pointers point to a location. Specifically, locations in computer memory. Think of it like this, when we create a variable (let's say it's an integer called foo as is so popular with programming theory) and give it a value of, for example 123, we have just that a variable with a value of 123. Now, if we setup a pointer to foo, then we have a way of indirectly accessing it. That is, we have a pointer of type int that points to foo which holds the value '123.' This would be done in code like so:
1. int foo = 123; // This is an integer variable 2. int *ptr = &foo; // This is a pointer to an integer variable Clear as mud? Dont sweat it. Pointers are hard - often considered the hardest thing to learn when picking up the C language. Pointers will eventually become second nature to you though, and there will be more on pointers within Objective-C further in this series.
Wrapping Up
You've just been given a crash-course overview of the C language fundamentals. This part of the series was intended to be a quick primer on C to get you ready and prepared for the rest of the series, and should have been especially useful to those who are already familiar with programming in another language. If you are new to programming in general or are still in doubt about any basic of C, re-read the above and feel free to leave questions in the comments. Before next time, be sure to try and compile your own programs using the code above. Set yourself small challenges, such as making a loop execute 10 times and count each time through the loop using printf. Theres no harm in trying and experimenting, if it goes wrong then its probably even better as itll get you on the right track to troubleshooting your own code.
Challenge
For this week, we will end on a simple challenge. You are to create three programs that count to 10 using each type of loop. Since we will use loops often in Objective-C, its good that you learn to create them by heart. That should be pretty easy, so try to count down from 10 to 1 afterwards (if ++ increments by one, what could be the code to decrement by 1?).
Next Time
In the next installment of this series, Ill provide an overview of how Objective-C works. Well also look at object orientated programming and its uses, as well as really drill down into classes, instances, methods, inheritance and more. Next week should really help you to understand what makes Objective-C such a great language and why it really extends the C language in so many useful ways.
Any Questions
If you have any questions, you can either leave a comment below where Ill try and keep checking or you can shoot me a message on Twitter (http://www.twitter.com/iDemonix) where Ill get back to you ASAP.
Welcome to part two of this introductory series on Objective-C. After spending last week reviewing the fundamentals of the C language upon which Objective-C is built, this week we will transition to focusing on what makes Objective-C such a great language for software development. Specifically, we will discuss the fundamentals of Object Oriented Programming (OOP) and demonstrate how to create a class and send messages to objects in Objective-C.
Methods
So we have an instance of a car, now that what do we do with it? Well, we drive it and fill it with petrol, among other things. Driving and filling with petrol apply only to the cars that we use, meaning that when we fill up a car or drive a car, we are only impacting one instance, and not all the cars in the world. Therefore, filling up the car instance is considered an instance method. Its something we do to our instance and only our instance. On the other hand, if we ask the original car class how many colors of car are available, this is a class method because we are no longer only talking about the car we drive around but all cars in general. Many of these principles become more clear with use, so lets look at a little bit of syntax.
In Objective-C, we call object methods by passing messages. When we want to know how much gas is in our instance of car, then we send a message to our instance and the message is the method we want to apply. Programmatically it looks like this: 1. [recipient message]; The brackets indicate we are sending a message. The first parameter is who should receive this message and the second parameter is what the message actually is. Finally, we end with a semi-colon as is common to most programming languages. So, with our previous example in mind, this is how we would interact with our instance of car to add gas to the tank; 1. [dansCar addGas]; The example above assumes that we have instantiated an instance of the Car class and named it dansCar. We then pass the addGas message to the object dansCar, which is the equivalent of calling a function. In another language, this line might look like: 1. dansCar.addGas();
Attributes
Lets say our car class has a gas tank thats stored as a percentage. For example, if the gas tank is at 50% then it is half-full and if it is at 100%, it means it is full to the brim. Now, if we want to know how much gas is in the tank, we dont just directly take that information from an attribute. Instead, we would use an accessor method to access the internal variable for us. Likewise, when we want to fill the tank, we dont just give the gas tank attribute a new percentage, we use a setter to update the attribute for us. This process is known as data encapsulation. What we mean by data encapsulation is that data is contained (so to speak) by methods meaning to access it we need to use methods. Some of you who have programmed in other languages and havent heard of data encapsulation may be wondering why we do things this way. The answer is that by encapsulating data, there is a nice cushion between the developer of a class and the user of a class. Because the class methods manage and maintains the attributes within the class, they can more easily maintain data integrity. Another major benefit is that when a developer distributes his class, the people using it dont have to worry about the internals of the class at all. A developer may update a method to make it faster or more efficient, but this update is transparent to the user of the class as he/she still uses the same method with no change to his/her code. This brings us nicely on to the next section were going to look at, which is how Objective- C separates interface from implementation.
When you create or work with a simple class in Objective-C you will see that it, by default, has two files. One is the implementation file which is a file that ends with a suffix of .m and the interface file which is a file that ends with a suffix of .h.
Interface
1. #import <cocoa cocoa.h=""> 2. 3. @interface Car : NSObject { 4. 5. //This is where attributes go 6. float fillLevel; 7. 8. } 9. 10. //This is where methods go 11. - (void)addGas; 12. 13. @end 14. </cocoa> First of all, were importing Cocoa.h which is a standard library with a lot of reusable code that we can use inside our app. Next, were declaring that this is the interface for the Car, but were also putting NSObject into that declaration. Adding : NSObject means that the Car class inherits from the NSObject class. Well talk more about inheritance in a future tutorial. Our instance variable fillLevel is declared next, and we specify that it is of the float data type so we can easily represent a percentage. The next line declares our addGas method. The - indicates that this is an instance method, not a class method. The (void) portion means that the method will not return anything back when it finishes executing. If the class was going to return an integer, this would be changed to (int) and the same for any other data type. Finally, we finalize the method declaration with a semicolon.
Implementation
1. 2. 3. 4. 5. 6. 7. 8. 9. #import "Car.h" @implementation Car -(void) addGas { // code goes here to add gas } @end
The implementation in this case contains the method to add gas to the tank. We also import Car.h, which is the interface file. Where our addGas method sits, we could add many more methods, but todays scope is to simply get you to understand how classes work rather than make a fully-fledged class.
Next Time
Next time well be looking more in depth at methods and at using variables with methods (as well as the basics of managing variables in Objective-C). This tutorial wasnt too long as its often a bit confusing for new developers as to why we separate classes in to more than one file. If youre feeling at all confused then please re-read the above or ask questions in the comments section below. Classes will be a constant reoccurrence in this series and its important you understand how they work.
Challenge
Seeing as this part of the series was fairly theoretical, theres not too much you can do to practice. However, I do recommend this week that you sign up for Apples developer website as its an invaluable reference. Once youve done that, have a snoop around some of their downloadable classes and download a few simple ones. You dont need to understand all of the code, just look at how the classes are formed and separated across files.
Welcome to part three of this series -I hope youre enjoying it! Last week we looked at how we separate classes in to separate files (interface and implementation), this week were going to look at classes again, but a little bit more in depth. Well also take a peak at inheritance and how it works, along with variable scope. So far, we have had some great feedback via email, twitter and comments. Its great to see so many people are interested in this subject and its even better to see that so many of you are trying it out for yourself and asking some great questions. Keep it up!
NSString is a string of text that is immutable. NSMutableString is a string of text that is mutable. NSArray is an array of objects that is immutable. NSMutableArray is an array of objects that is mutable. NSNumber holds a numeric value.
Well learn many more later on, but for now the above will come in handy. Youre probably wondering what mutable and immutable means, which is an entirely reasonable question. If an object is immutable that means when we create the object and assign a value then it is static. The value can not be changed. If an object is mutable then it is dynamic, meaning the value can be changed after creation.
This may seem a little confusing at first. In the first version, we have nested the statements within brackets on the same line, whereas in the second we have separated the statements into two lines. The method init initializes all the instance variables in the class. 1. testString = @"Here's a test string in testString!"; This line is pretty self explanatory, the reason we prepend the quotes with an @ sign is to tell the compiler that the following text is an NSString. NSLog(@testString: %@, testString); Here we log some information to console. XCode has a debugger built in that you can find under the Run menu. It is very useful when developing an application to log when events are happening and the values of certain variables it can help when troubleshooting your application and debugging problems. This method works like printf (remember from the first week?) where we supply a string of text with a replacement character (%@ means an Objective-C object).
Finally we return 0, which we know just tells the operating system that the application ended with no problems.
Inheritance
Remember when we made our NSString earlier, we used the init method? Well NSMutableString, NSArray and in fact, every single NS class, also uses init. Seems a lot of wasted code to put the init method in each class, right? It would be, thats why init is usually only implemented once, in the root class known as NSObject. Because classes inherit from each other, a class that is created as a child of another, parent class will automatically gain access to the parent class methods.
Lets take NSMutableString for example. NSMutableString has NSString for a parent (making it a child), meaning it inherits from NSString. Meanwhile. NSString has NSObject as a parent, so it inherits from NSObject.
So for example, NSObject has a method called init, so each subclass has this method implemented which is called a class method. As a matter of fact, the init method in NSObject doesnt actually do anything it simply returns self. The reason for this is that methods can be overwritten. So the NSArray class may override the init that it inherits to add functionality to it such as making sure memory is available or preparing any instance variables it may need. As demonstrated, this is useful because it means that in addition to inheriting from classes we can also extend classes. When we extend a class, we take an existing class and add additional functionality to what is already available. This means you could create your own version of NSString with additional methods, such as a method to fill the string with random text or perform some sort of character encoding.
Summary
At this point, the fundamentals of how classes work should be clear. To test your comprehension, see if you can answer the following questions in your mind:
What is the difference between a class and an object? Why do we use classes? Why is inheritance useful?
Since classes are such an important part of Objective-C, its important to really feel comfortable working with them. Last week we started looking at classes and this week we have gone into more depth. Next week, you may be glad to hear, were going to move away from the theoretical side and start working on our own simple class or two to perform simple tasks.
Homework
Since we have mainly done theory so far, your homework this week is to surf Apples developer website (you should have done this last week) and look at some of the classes available. If you dont know where to start, start with something such as NSString. Youll become more comfortable with the details of the parent class, the methods, and so on. This will be important later on when youre using classes outside of this series and you want to know what methods they inherit or use.
Next Time
Well get more practical next week with some class coding. Classes really are central to Objective-C, so its mega important that you come to grips with them and the goal of this series is to really ensure that you do! As usual, if you have any questions or comments, you can reach me by dropping a comment or email. If youd like to get in touch with me personally and get a quick answer, send me a tweet!
Welcome to part four of this series on Objective-C. So far, weve looked a lot at theory and the principles and functionality of the language to get a good idea of how it works. Today, we
will be making a simple class similar to the car example we looked at in previous parts of this series. Our class will take the details of a car, allowing us to get and set the values held. After todays example you should be able to create your own classes in Xcode and toy around with them. So far, we have had some great feedback via email, twitter and comments. Its great to see so many people are interested in this subject and its even better to see that so many of you are trying it out for yourself and asking some great questions. Keep it up!
Getting Started
Start by firing up Xcode and creating a new project. Under the Mac OS X separator, click Application, then click Command Line Tool. Finally, change the drop down box to set the type to Foundation.
Save the project as whatever you want, I called mine CarApp. Once the project window appears, we need to create a new class. Hit Command-N (or File > New File), navigate to Cocoa Class under Mac OS X and select Objective-C class. Make sure Subclass of is set to NSObject and press Next. Name your class SimpleCar and ensure that a .h file will be created, then save it.
Our class now exists, but it does nothing. Lets change that by giving it some code. Remember that in Objective-C we split our code into two parts: interface and implementation. It makes logical sense to work on the interface first, so thats where well start.
First of all, were including Cocoa.h, which gives us access to such things as NSString, NSMutableString, etc. Then, we create our class (SimpleCar) as a subclass of NSObject. Now we need to decide on what information our class needs to store. Since were using a car as our example we need to store car-related information, such as:
Theres a lot more we could go into, but for now that will do. For each of these properties, we need to store them in a variable suited for that type of data. Make and model will be a range of characters (such as text, number and possibly punctuation) so it makes sense to use a string. The VIN (Vehicle Identification Number) will only be a number so thats what well use. Our code now looks like this (header omitted): 1. @interface SimpleCar : NSObject { 2. NSString* make; 3. NSString* model; 4. NSNumber* vin; 5. } 6. 7. @end We previously said that in order to get or set data from a class, a method should be used. So to set the variables, we need to add methods. To do this, well make four: one will set the make, one the model, one the VIN, and a final method will set both make AND model (just to show you how to use multiple arguments).
1. @interface SimpleCar : NSObject { 2. NSString* make; 3. NSString* model; 4. NSNumber* vin; 5. } 6. 7. // set methods 8. - (void) setVin: (NSNumber*)newVin; 9. - (void) setMake: (NSString*)newMake; 10. - (void) setModel: (NSString*)setModel; 11. 12. // convenience method 13. - (void) setMake: (NSString*)newMake 14. andModel: (NSString*)newModel; 15. 16. @end We declare methods after the curly bracket and before @end. By placing a dash (minus sign) before the method, we tell the compiler were about to declare an instance method. An instance method is a method executed on our instance. Conversely, a plus sign indicates that the method being invoked is a class method that does not need an individual object instance to execute -more on this later. Our first method returns void, is called setVin and takes an NSNumber as an argument. Our second method is similar, it returns void, is call setMake, and takes an NSString as an argument. The third is the same, with a different name. Our final method also returns void but takes two parameters: newMake and newModel, both of which should be NSString. The naming used in this method is similar to how most Objective-C methods are named: in plain English. So when you read the method allowed its obvious that the method will Set make and model. Its important to remember that the method name in this case is setMake:andModel: all the argument titles are included in the method name. An important note is that we use (void) because our methods do not need to return anything. Since all they are doing is setting data and do not need to return anything back (such as a success message) we simply use void. Next, we will add the methods we will use to get the values. Although we call our methods get and set methods, we only usually use set in the title and omit get. How you name your methods is ultimately up to you, but dropping get is common and helps avoid confusion. Our new set of methods looks like this:
1. // set methods 2. - (void) setVin: (NSNumber*)newVin; 3. - (void) setMake: (NSString*)newMake; 4. - (void) setModel: (NSString*)newModel; 5. 6. // convenience method 7. - (void) setMake: (NSString*)newMake 8. andModel: (NSString*)newModel; 9. 10. // get methods 11. - (NSString*) make; 12. - (NSString*) model; 13. - (NSNumber*) vin; Notice that the get methods use the same names as the variables in the class. This will make it simple when we fetch the variables. It will be as if were accessing the variables directly, essentially making the get methods appear transparent.
1. @implementation SimpleCar 2. 3. // set methods 4. - (void) setVin: (NSNumber*)newVin { 5. 6. } 7. 8. - (void) setMake: (NSString*)newMake { 9. 10. } 11. 12. - (void) setModel: (NSString*)newModel { 13. 14. } 15. 16. - (void) setMake: (NSString*)newMake 17. andModel: (NSString*)newModel { 18. 19. } 20. 21. // get methods 22. - (NSString*) make { 23. 24. } 25. 26. - (NSString*) model { 27. 28. } 29. 30. - (NSNumber*) vin { 31. 32. } 33. 34. @end Now we need to give our methods some code. Lets start with the getter methods as theyre straightforward enough. For each getter method, all we need to do is make sure that the function returns what it is intended to return. For this reason, our getter methods look like this:
1. 2. 3. 4. 5. 6. 7. 8.
9. - (NSNumber*) vin { 10. return vin; 11. } Remember: the methods are returning the variables we defined in the interface file. Dont get confused between the method names and the variable names. Thats pretty straightforward, when we call make (for example), then make returns the pointer to an NSString in this case to the make variable. The same happens for model and vin (except of course vin returns a number). Now for the setter methods, first well look at the code and then well go through it afterwards. Our setter methods look like this: 1. // set methods 2. - (void) setVin: (NSNumber*)newVin { 3. 4. [vin release]; 5. vin = [[NSNumber alloc] init]; 6. vin = newVin; 7. 8. } 9. 10. - (void) setMake: (NSString*)newMake { 11. 12. [make release]; 13. make = [[NSString alloc] initWithString:newMake]; 14. 15. } 16. 17. - (void) setModel: (NSString*)newModel { 18. 19. [model release]; 20. model = [[NSString alloc] initWithString:newModel]; 21. 22. } 23. 24. // convenience method 25. - (void) setMake: (NSString*)newMake 26. andModel: (NSString*)newModel { 27. 28. // Reuse our methods from earlier 29. [self setMake:newMake]; 30. [self setModel:newModel]; 31. 32. } The set methods are a bit trickier than our get methods. We want to alloc the values that are passed into each method so that they are owned by the class. We first release these variables in case they are already allocd. If they are not allocd, then they are nil, and nil objects ignore
messages passed to them. We will cover these issues more when we discuss memory management. Because we actually allocated memory for our objects in the setter methods, we need to be sure we release them when the object is released from memory. To do this, we need to add a custom dealloc method, like so: 1. -(void) dealloc 2. { 3. [vin release]; 4. [make release]; 5. [model release]; 6. [super dealloc]; 7. }
1. #import <Foundation/Foundation.h> 2. #import "SimpleCar.h" 3. 4. int main (int argc, const char * argv[]) { 5. 6. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 7. 8. SimpleCar *myCar = [[SimpleCar alloc] init]; 9. 10. NSNumber *newVin = [NSNumber numberWithInt:123]; 11. 12. [myCar setVin:newVin]; 13. [myCar setMake:@"Honda" andModel:@"Civic"]; 14. 15. NSLog(@"The car is: %@ %@", [myCar make], [myCar model]); 16. NSLog(@"The vin is: %@", [myCar vin]); 17. 18. [myCar release]; 19. 20. [pool drain]; 21. 22. return 0; 23. } First of all we create a pointer to an instance of SimpleCar called myCar. Next we use alloc and init these will be discussed later on down the line. Next, since we need to pass an NSNumber to the setVin method, we make one here. Again we create a pointer to an NSNumber instance called newVin and we initiate it with the integer value of 123. The constant 123 is an integer, which is why we use numberWithInt. Next, we invoke our methods, first of all we put who should receive the message (myCar) and then we use the method setVin. After the colon is the value we are supplying to the method which is the NSNumber we created before. Next we do the same but call the setMake method with two parameters. The reason these parameters are preceded by an @ sign is to tell the compiler that the following is a string. Finally, we release myCar as we are done with it more on this later in the series under memory management. Our class is now working, and in order to see the proof, we added some NSLog statements to print the values to the console. If you open up the console (Run > Console) and then build and run your app, you should see output similar to this:
are dynamically created for our instance variables (we could of used writeonly or readonly for just one or the other). The reason we use retain will become clear next time when we cover memory management. Before this can work, we need to implement it in our implementation file, we do this using @synthesize. Our new implementation file looks like this: 1. #import "SimpleCar.h" 2. 3. @implementation SimpleCar 4. 5. @synthesize make, model, vin; 6. 7. - (void) setMake: (NSString*)newMake 8. andModel: (NSString*)newModel { 9. 10. [self setMake:newMake]; 11. [self setModel:newModel]; 12. 13. } 14. 15. @end Doesnt that look better? Think of it like this, @property replaces all of the interface method declarations for getters and setters, and @synthesize replaces the actual methods themselves. The getters and setters are now dynamically created and we dont need to waste time creating them unless we need to do something really special.
Wrapping Up
You should now have a firm grip of classes, objects and instances. Sure, youre not creating classes that will change the world yet, but this stuff takes time. Its better to learn by example, so if youre not coding as you go along then be sure to at least download the source files and have a read through (and a compile) to ensure youre 100% on whats going on.
Next Time
Weve mentioned memory management a lot in this tutorial, its a very important subject that needs to be addressed (pun intended), so well dive in to that next time. True, it isnt the most fun subject or the easiest to come to terms with, but its absolutely crucial if you want to become a skilled Objective-C programmer.
Challenge
This weeks challenge may be a little tricky, but well see how you get on. First of all, if you havent copied all the code above, download the source files that are included with this article. The challenge is to add another class to the project, but this time it should be a subclass of SimpleCar (remember, we define the parent class in the interface file). If you can do that, play
around and use the inherited methods and try to add your own for things such as: engine size, doors or height. Remember: if you have any questions or queries, drop a comment below or shoot me a message on Twitter. The only stupid question is the one you didnt ask this series is about learning so feel free to ask away!
Welcome to part five of this series on Objective-C. Today were going to look at memory management, an element of Objective-C (and many other languages) that tends to trip up newer programmers. Most scripting languages (such as PHP) take care of memory managaement automatically, but Objective-C requires that we are careful with our use of memory and manually create and release space for our objects. Its good practice to keep track of how much memory your application is using, so you dont encounter any leaks or hog up the memory on the system. Its even more important on mobile systems such as the iPhone where memory is much more limited than on a desktop machine.
Two Approaches
In Objective-C there are two methods for managing memory, the first if reference counting and the second is garbage collection. You can think of them as manual and automatic, as reference counting is code added by the programmer and garbage collection is the system automatically managing our memory. An important note is that garbage collection does not work on the iPhone, which is why we will not look at how it works. If you should wish to program for the Mac, then its worth looking at Apples documentation to see how garbage collection works.
Reference Counting
So, how do we manage our memory in our apps? First of all, when do we use memory in our code? When you create an instance of a class (an object), memory is allocated and our object can now function correctly. Now one little object might not seem that great of a deal, but when your apps grow in size it quickly becomes an enormous problem. Lets look at an example, say we have some sort of drawing app and each shape the user draws is a separate object. If the user has drawn 100 shapes, then we have 100 objects sat in memory. Now lets say the user starts over and clears the screen, then draws another 100 objects. If we dont manage our memory properly, were going to end up with 200 objects doing nothing more than hogging memory. We counter this by reference counting. When we create a new object and use alloc, our objects have a retain count of 1. If we call retain on that object, the retain count is now 2 and so on. If we release the object, the retain count decrements back to 1. Whilst the retain count is non-zero, our object will stick around, but when the retain count hits zero, the system deallocates our object freeing up the memory.
Syntax
There are various methods you can call that will have some effect on memory management. First of all, when you create an object using a method name that contains alloc, new or copy, you take ownership of that object. This is also true if you use the retain method on an object. Once you release, or autorelease (more on that later) an object, you no longer take ownership of that object or care what happens to it. So, if we alloc an object like so;
1. myCarClass *car = [myCarClass alloc]; We are now responsible for the object car and we must manually release it later (or autorelease it). Its important to note that if you were to try and manually release an object that has been set to autorelease, the application would crash. Since we created our object using alloc, our car object now has a retain count of 1, meaning it will not be deallocated. If were to also retain our object like so; 1. [car retain]; Then our retain count is now 2. So in order to get rid of the object, we need to release twice to set the retain count to 0. Since the retain count is now zero, the object will be deallocated.
drain message and not have to worry about manually releasing to deallocate. Apple have a great example of when youd use this kind of nested autorelease pool in their documentation; 1. void main() 2. { 3. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 4. 5. NSArray *args = [[NSProcessInfo processInfo] arguments]; 6. 7. for (NSString *fileName in args) { 8. 9. NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; 10. 11. NSError *error = nil; 12. NSString *fileContents = [[[NSString alloc] initWithContentsOfFile:fileName 13. encoding:NSUTF8StringEncoding error:&err or] autorelease]; 14. 15. /* Process the string, creating and autoreleasing more objects. */ 16. 17. [loopPool drain]; 18. } 19. 20. /* Do whatever cleanup is needed. */ 21. [pool drain]; 22. 23. exit (EXIT_SUCCESS); 24. } The above example has a bit more than we need, but the structure is there. As you can see, when the application is opened and main is loaded, an autorelease pool called pool is created. Meaning anything autoreleased before the pool is sent the drain message will be assigned to this autorelease pool, unless it is inside an autorelease pool inside this one (sorry if that sounds a little confusing). Inside the loop, another autorelease pool is created called loopPool. This pool is drained inside the loop, so anything autoreleased inside the loop is released before the loop iterates (or ends). The inner autorelease pool has absolutely no effect on the outer autorelease pool, you may nest as many autorelease pools as you need. If we used autorelease in the loop above, but did not have a separate autorelease pool, then all the objects we were creating would not be released until the end of main. So if the loop ran 100 times, we would have 100 objects hogging up memory that have yet to be released bloating our application.
retainCount
Before we wrap up, lets look at something that could help make memory management an easier-to-swallow chapter. So far when weve created objects, weve remembered how many references an object has and so on but weve never seen an actual number. For the purposes
of education, there is a method we can use to see how many references an object has called retainCount. The way we print a retainCount for an object is like so; 1. NSLog(@"retainCount for car: %d", [car retainCount]); retainCount returns an integer, so we use %d to display it in the console. There are rare instances (which we wont go in to) where retainCount can be wrong and as such should not be 100% relied upon programatically. It is implemented for debugging only, so an app should never go live using the retainCount method.
Wrapping Up
Memory management is a subject that many new programmers find difficult, especially programmers who come from languages that take care of it all for you. Weve covered the basics, which should be enough to allow you to find your feet and start incorporating memory management in to your apps. Apple has a fantastic developer documentation library available on their developer website, which I highly recommend you check out if youre unclear on anything we touched on today. Weve tried to keep the tutorial short and laser-focused today to help you understand memory management with no other fluff added. Questions are welcome, as usual.
Challenge
Simply experiment with the console by creating a simple object that contains a few synthesized variables, create a few instances of this class and then check the retain count using the retainCount method. The best way to understand memory management is to fire up XCode and play around with allocs and retains etc, remember that crashes and mistakes arent a brick wall as theyll ultimately help you avoid the mistakes in future.
Next Time
In the next installment well be looking at categories, a great feature available in Objective-C that can save developers a lot of time and make for more simplistic code.
In todays tutorial, you will learn about Categories and how to use them to extend the functionality of Cocoa-Touch classes. This is our final installment in the Learn Objective-C series, so you will also be provided with a quick recap on what weve covered so far and then look at what you can do to further your skills as an Objective-C or iPhone application developer.
Categories
So, what are categories? A lot of Objective-C tutorials and series will overlook categories, which is a shame as theyre such a useful language feature thats amazingly useful. Categories help keep code clean and less cluttered by eliminating the need for unnecessary subclassing. From what weve learned so far, if we had an NSString object that we wanted to add functionality to do something like add a method that would replace all the a characters with a 4 (useless I know, but for example purposes only), then we could subclass NSString and add the method ourselves. We also saw this kind of subclassing when we made our car class, I asked you at the end of that tutorial to make a subclass of car so you could add functionality to the base class. Subclassing is a good approach, and I am in no way telling you to stop subclassing, but, in certain situations, categories provide a better approach to add some extra functionality to a class. Categories allow us to add methods to an existing class, so that all instances of that class in your application gain your functionality. For example, say we have 100 NSString objects in your app, but youd like to make a custom subclass so each NSString has an extra method (reverseString for example). With categories we can simply add the method in a category and all instances will be allowed to use the new method. The syntax is obviously slightly different from subclassing and categories dont allow you to use instance variables. However, it is possible to overwrite a method already in place, but this should be done with caution and only if doing so is really necessary.
Syntax
Categories follow the same syntax layout as a class, as in they have an implementation and an interface. The interface looks like this: 1. @interface ClassNameHere (category) 2. 3. // method declaration(s) 4. 5. @end 6. 7. The implementation looks like this; 8. 9. @implementation ClassNameHere (category) 10. 11. // method implementation(s) 12. 13. @end
Example
Easy, right? So lets look at an example. Were going to make a simple method that will reverse a string. While this method is practically useless; its not what the method is doing thats important. You should already know what the interface will look like, but here it is anyway: 1. @interface NSString (reverse) 2. 3. -(NSString *) reverseString; 4. 5. @end Now to create our implementation: 1. 2. 3. 4. 5. 6. 7. @implementation NSString (reverse) -(NSString *)reverseString { } @end
Next, well need to add some simple code to flip the string around. Since were adding methods to the NSString class, we reference the string using self. Now in order to reverse the string well need a new, temporary string object to hold the reversed string. The way we will reverse the string is to simply loop through the existing string in reverse, each time we find a new character well add it to the reversed string object. The code for this is: 1. 2. 3. 4. 5. 6. 7. int length = [self length]; NSMutableString *reversedString; reversedString = [[NSMutableString alloc] initWithCapacity: length];
while (length > 0) { [reversedString appendString:[NSString stringWithFormat:@"%C", [self character AtIndex:--length]]]; 8. } 9. 10. return [reversedString autorelease]; This method is fairly simple, were looping through once for each character in the string. We append the current character using stringWithFormat and the character (C) identifier. When we call length we not only return the length integer, we also subtract from it too, which is how our loop moves along the string. If all has gone well then all NSStrings already in our project should adhere to our new category. Now you can see why categories are so useful!
1. @interface NSString (reverse) 2. 3. -(NSString *) reverseString; 4. 5. @end 6. 7. @implementation NSString (reverse) 8. 9. -(NSString *)reverseString { 10. int length = [self length]; 11. NSMutableString *reversedString; 12. 13. reversedString = [[NSMutableString alloc] initWithCapacity: length]; 14. 15. while (length > 0) { 16. [reversedString appendString:[NSString stringWithFormat:@"%C", [self charact erAtIndex:--length]]]; 17. } 18. 19. return [reversedString autorelease]; 20. } 21. 22. @end Each block of code (interface and implementation) should be in their own respective files by convention. However, we name categories a little differently. The two files we have created are named: NSString+reverse.h (interface) and NSString+reverse.m (implementation). This is a typical naming convention following the pattern of the name of the class we are adding a category to, a plus sign, and the name of our category. Before we continue, remember that we still need to include our header file into the main project. So now our code should look something like this: 1. NSString* testString = @"Just a test"; 2. 3. [testString reverseString]; 4. 5. NSLog(@"Reversed: '%@'", testString); If all went according to plan, then the Console should log a reversed version of Just a test!
If all doesnt go according to plan, check to be sure youve copied the code for reverseString properly and make sure youre including the header (*.h) file for the category in your main code file. As you can see, categories really are quite useful (especially with strings). They have a wide range of uses in any project, a common one I use is validation. This way I can keep all of my validation in one place and dont have to use any complicated subclasses.
Fundamentals of Programming Fundamentals of OOP Classes and Subclasses Memory Management Good Practice Some Standard Naming Conventions
I hope youve enjoyed this series, and if youve only read one or two of the tutorials so far, then I encourage you to start at day 1 and read a tutorial a day. By the end of the series you should be confident enough to start writing your own code in just under a week! If you have any questions about the series or examples shown, or if you would like an additional tutorial on another area of Objective-C programming, feel free to leave a comment below. If you have any questions on Objective-C itself, a project that youre working on, or just some technical advice, dont hesitate to drop me a mention or direct message on Twitter or visit my website and find your way to the contact form!
Challenge
Now that were finished, the challenges are endless. I highly suggest you put pen to paper and decide on a first app to build from start to scratch. Come up with an idea and a design, then apply all the principles you have learned from this series and let us know what you come up with! Thanks for reading!