iOS100
Exercise 2
"randomItems"
Objectives
Create a command line tool that generates an array of "randomItem" objects and logs out randomly generated bits of information for each item to the console.
Things you will learn...
- Create a command line tool
- Create and populate an array
- Iterate though an array
- Subclass an Objective-C class
- Implement class and instance methods
- Override methods
- Implement Initializers
- And so much more!
End Result
Each item will contain:
- Item Name
- Serial Number
- Value in Dollars
- Timestamp
Step 1
Create a command line tool...
Setting up an array
- Setup 5 objects
- 1 NSMutableArray
- 4 NSStrings
- Remember: An array does not contain the objects that belong to it, only pointers to where those objects are stored in memory.
Things to remember...
- NSMutableArray is a subclass of NSArray
- It inherits most of its functionality from NSArray
- Classes exist in a hierarchy
- Every class has exactly one superclass
- Except for the root class of the entire hierarchy
- A class inherits the behavior of its super class
- Every class inherits the methods and instance variables defines in NSObject
Subclasses
- Every subclass adds methods and instance variables to extend its superclass...
- NSString - Adds behavior for storing and handling string, including the method "length"
- NSArray - Adds behavior for ordered lists, including accessing an object at a given index w/ "objectAtIndex:" and getting the number of objects in the array via "count"
- NSMutableArray - Extends NSArray's abilities to add and remove pointers.
Lets create and populate the array...
Iterating over an array
- Option 1
- Traditional for loop
- Leverage the count method in NSArray
- Create a new NSString object every time you iterate
- Access array element though "objectAtIndex:" or [ ]
- Option 2
- Fast enumeration
- Shorter syntax
- Less error prone
- Has one limitation
- Can not be used in conjunction with adding or removing items from an array
Loop though the items array...
Another way to view whats in an object...
Use the object pointer format string (%@) in NSLog()
This calls the "description" method on any object that is passed
By default, every subclass of NSObject (all objects) call the "description" method which returns an instance of NSString and replaces the %@ token.
Subclassing an Objective-C Class
Lets create a subclass of NSObject called "BNRItem"
BNRItem will represent something that a person owns in the real world like a laptop, bike, or backpack
BNRItem is going to act as a model class and hold data about our various possessions
Lets create our NSObject subclass...
BRNItem Class
- BNRItem.h
- Header file (AKA interface file)
- Declares...
- Name of the class
- It's superclass
- It's instance variables
- Any methods that belong to the class
- BNRItem.m
- Implementation file
- Contains all of the code for the methods that the class implements
Instance Variables
An item in the real world is going to contain...
-
Name
-
Serial Number
-
Value in Dollars
-
Date Created
Lets add them to our class!
Accessing Instance Variables
Need a way to access them...
Accessors!
"getters" & "setters"
Allow us to get and set the values of our instance variables though messaging
Without these, objects can access the instance variables of other objects...
Creating Getter & Setter Accessors...
- In Objective-C the name of a setter method is "set" + the capitalized name of the instance variable
- Getters are just named the same name as the instance variables
Implementing Getters & Setters...
- Setters
- Should set each instance variable = to the value of the variable passed into the method
- Getters
- Should return the value of the instance variable
Lets add getter and setter methods to our class...
(Note: dateCreated should be readonly - Does not need a setter method)
Adding BRNItem to main.m
In order to use our custom subclass,
we must import it into the main.m file
Now lets use it!
What happened!?
When an object is created, all of its instance variables are "zeroed-out"
Pointers to objects are made = nil
Primitives are set to their defaults such as 0
To make these variables more interesting, we have to pass arguments to their setter methods...
Using dot syntax
When using dot notation, the receiver (item) is followed by a "." followed by the name of the instance (valueInDollars)
This syntax is the same for both the getter and setter on an instance variable
Using dot notation saves you from having to write a lot of boilerplate code!
Lets try it!
Overriding Methods
Subclasses can override methods of their superclass
One common example is overriding the description method inherited from NSObject
By default, this method returns the objects class and location in memory
NSLog(@"%@", item.description) // displays "<BNRItem: 0x100102f80>"
This is usually not that helpful...
Lets override it to show a real description of the object...
Initializers
- By default, every class has one way to initialize a new instance - the init method
- Initializers take arguments that a class can use to setup an object for use
- You can add as many initialization methods as you want!
- Each initializer should begin with the "init" prefix
Lets declare two initializers in our BRNItem header
- Takes thee arguments that it will use to configure the items name, value, and serial number
- Takes the items name and assigns the rest of the values defaults
The designated initializer...
No matter how many initializers you have, one must act as the designated initializer
The designated initializer makes sure that every instance variable of an object is valid
In the case of BRNItem,
- (instancetype)initItemWithName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber;
should be the designated initializer since it is setting most of the instance variables defaults.
What is instancetype?
-
This keyword can only be used for return types
-
It is used to match the return type of the receiver
-
init methods always declare a return type of instancetype
-
This prevents conflicts with inheriting inits of the class in a different subclass
What about id?
id is defined as "a pointer to any object"
-
Unlike instancetype, id can be used as more then just a return type
-
It can be used for:
-
Return types
- Variable declarations
- Great for when you are unsure of what type of object the variable will end up pointing to
// Example: "item" could be any type of object...
for (id item in items) {
NSLog(@"%@", items);
}
Implement your designated initializer...
Things to note...
- Always call the superclass's designated initializer using "super"
- Always return a pointer to the successfully initialized object using "self"
What is "self"?
- "self" is an implicit (predeclared) local variable automatically set to point to the object that was sent the message.
- You should always return self in the last line of the init method so the caller can assign it to a variable
-
In most other languages it is known and used as "this"
Typically "self" is used so an object can send messages to itself:
- (void)chickenDance
{
[self pretendHandsAreBeaks];
[self flapWings];
[self shakeTailFeathers];
}
What about "super"?
When you are overriding a method, you often want to keep what the method of the super class is doing and have your subclass add something new on top of that...
- (void)someMethod
{
[super someMethod];
[self doMoreStuffMethod];
}
In BNRItem, the designated initializer is sending the init message to "super", which calls NSObjects implementation of "init"
Other initializers and the initializer chain
Note that this class already has a third initializer called "init" which it inherits from NSObject.
We must override "init" if we want to be able to use our own initializers....
Lets implement our second initializer "initWithItemName"
This initializer will only set the value of itemName and then set default values for the other arguments
Chain of initializers
Using initializers in a chain reduce the possibility of error and makes maintaining your code easier.
Simple initializer rules...
- A class inherits all initializers from it super class and can add as many as it wants for its own purposes
- Each class picks one initializer as its designated initializer
- The designated initializer calls the superclass's designated initializer (directly or indirectly) before doing any initializing of its own
- Any other initializers call the class's designated initializer (directly or indirectly).
Lets implement our initializers!
Class vs Instance Methods
- Methods have two types
- Instance
- Operates on a particular instance of a class
- Invoked by sending a message to a specific instance of a class
- Example: init method
- Class
- Typically creates a new instance of the class or retrieves a global property of the class.
- Invoked by sending a message to the class itself
- Example : alloc method
Lets create a public class to generate random items!
What is a property?
-
Serves as a replacement for having to write getter and setter methods!
-
A property has three parts
-
@property
-
Property attributes
- Type and name
@property (nonatomic, retain) NSArray *questions;
Property Attributes
- Property attributes are special keywords to tell compiler how to generate the getters and setters.
- Thread Safety
- All property declarations by default are "atomic" – meaning they are thread-safe.
- However, in general, most applications are not multithreaded. With that in mind, we the "nonatomic" attribute to indicate that the property accessor methods should not protect against thread safety.
@property NSString *someString; //Thread safe
@property (atomic) NSString *someString; //Thread safe
@property (nonatomic) NSString *someString; // NOT thread safe
Read/Write Attribute
- Options
- readwrite
- Implements a getter and a setter method
- readonly
- Only implements a getter method
- Default is set to readwrite
Memory Management
- Describes the type of reference that the object with the instance variables has to the object that the variable is pointing to.
-
strong
-
Specifies that there is a strong (owning) relationship to the destination object.
-
weak
-
Specifies that there is a weak (non-owning) relationship to the destination object.
-
If the destination object is deallocated, the property value is automatically set to nil.
- Default is strong
Lets Implement Properties!
What did we learn today!?
Bonus
Run your application in the terminal!
"./randomItems"