2011年6月22日 星期三

Stanford CS193P Note-03

Foundation Framework

- (NSString *)description is a useful method to override (it's %@ in NSLog()).

NSString
    1. International (any language) strings using Unicode.
    2. Used throughout iOS instead of C language's char * type.
    3. An NSString instance can not be modified! They are immutable.
    4. Usual usage pattern is to send a message to an NSString and it will return you
       a new one.

NSMutableString (Mutable version of NSString.)
    Can do some of the things NSString can do without creating a new one
    (i.e. in-place changes).

NSNumber
    1. Object wrapper around primitive types like int, float, double, BOOL, etc.
    2. ex:
       NSNumber *num = [NSNumber numberWithFloat:36.5];
       float f = [num floatValue];

◎ NSValue
    1. Generic object wrapper for other non-object data types.
    2. ex:
       CGPoint point = CGPointMake(25.0, 15.0);
       NSValue *val = [NSValue valueWithCGPoint:point];

NSData (Bag of bits)
    Used to save/restore/transmit data throughout the iOS SDK.

NSDate
    Used to find out the time right now or to store past or future times/dates.
   
NSArray
    1. Ordered collection of objects.
    2. Immutable. That's right, you cannot add or remove objects to it once it's created.
    3. Some methods:
       - (void)makeObjectsPerformSelector:(SEL)aSelector;
       ex: [array makeObjectsPerformSelector:@selector:(doSomething)];

       -(id)lastObject; //returns nil if there are no objects in the array(convenient)

NSDictionary
    1. Hash table. Look up objects using a key to get a value.
    2. Immutable. That's right, you cannot add or remove objects to it once it's created.
    3. Keys are objects which must implement - (NSUInteger)hash &
       - (BOOL)isEqual:(NSObject *)obj

    4. Keys are usually NSString objects.

NSSet
    1. Unordered collection of objects.
    2. Immutable. That’s right, you cannot add or remove objects to it once it’s created.
    3. Some methods:
       - (id)anyObject; // 隨機丟出一個 object
      
-----------------------------------------------------------------------------------------------------

Enumeration
    1. Looping through members of a collection in an efficient manner
    2. Language support using for-in (similar to Java)
    3. Example: NSSet of id (could just as easily be an NSArray of id)
       NSSet *mySet = ...;
       for (id obj in mySet) {
           // do something with obj, but make sure you don't send it a message it does
           // not respond to

           if ([obj isKindOfClass:[NSString class]]) {
               // send NSString messages to obj with impunity
           }
        }
        // or use: isMenberOfClass:

Property List (.plist)
    1. The term "Property List" just means a collection of collections
    2. Specifically, it is any graph of objects containing only the following classes:
       NSArray
, NSDictionary, NSNumber, NSString, NSDate, NSData
    3. Why define this term?
       Because the SDK has a number of methods which operate on Property Lists.
       Usually to read them from somewhere or write them out to somewhere.
       [plist writeToFile:(NSString *)path atomically:(BOOL)];
       // plist is NSArray or NSDictionary


NSUserDefaults
    1. Lightweight storage of Property Lists.
    2. It's basically an NSDictionary that persists between launches of your application.
       Not a full-on database, so only store small things like user preferences.
    3. Read and write via a shared instance obtained via class method
       standardUserDefaults:
       [[NSUserDefaults standardUserDefaults] setArray:rvArray
       forKey:@"RecentlyViewed"];

    4. Always remember to write the defaults out after each batch of changes!
       [[NSUserDefaults standardUserDefaults] synchronize];

-----------------------------------------------------------------------------------------------------

Objects

Creating Objects
It is a two step operation to create an object in Objective-C
    1. Allocating (almost always done with the NSObject class method alloc)
    2. Initializing (done with a method that starts with the four letters init)
alloc makes space in heap for the class's instance variables
    Also sets them all to zero, so instance variables which are object pointers
    start out nil

◎ Each class has a "designated" initializer method
    1. NSObject's is init (by convention, all init methods start with the four letters init)
    2. When you subclass an object, you must call your superclass's
       designated initializer from your designated initializer
       (and make sure that it did not fail and return nil)
    3. All init methods should be typed (in their declaration) to return id
       (not statically typed)
    4. Callers should statically type though, e.g., MyObject *obj = [[MyObject alloc] init];

Initializing Objects
◎ Example: A direct subclass of NSObject
    @implementation MyObject
    - (id)init
    {
        if (self = [super init]) {
            // initialize our subclass here
        }
        return self;
    }
    @end

A Tale of Initializer
    @implementation MyView
    - (id)initWithFrame:(CGRect)aRect {
        if (self = [super initWithFrame:aRect]) {
            // initialize my subclass here
        }
        return self;
     }

    - (id)initToFit:(Shape *)aShape {
        CGRect fitRect = [MyView sizeForShape:aShape];
        return [self initWithFrame:fitRect];
    }
    @end

-----------------------------------------------------------------------------------------------------

Object Ownership

◎ When do you take ownership?(所有權)
    1. You immediately own any object you get by sending a message starting
       with new, alloc or copy.
    2. The most common one of these is, of course, the combination
       of alloc followed by init...
    3. If you get an object from any other source you do not own it, but you can take
       ownership by sending it the NSObject message retain.

◎ How do you give up ownership when you are done?
    1. Send the object the NSObject message release.
    2. Do not send release to an object you do not own. This is very bad.

◎ ex for retain count:
    NSString *str = [[NSString alloc]initWithString:@"foo"]; // retain count: 1
    NSString *strB = [str retain];                             // retain count: 2
    [str release];                                              // retain count: 1
    [strB release];                                 // retain count: 0 ; it will invoke [str dealloc];

Temporary Ownership
◎ So how does this "temporary ownership" thing work?
    1. If you want to give someone an object with the "option" for them to take
       ownership of it, you must take ownership of it yourself, then send the
       object the message autorelease.
    2. Your ownership will "expire" at some future time
       (but not before the current event is finished).
       In the meantime, someone else can send retain to the object if they want to
       own it themselves.

◎ Best understood by example
    - (Money *)showMeTheMoney:(double)amount {
        Money *theMoney = [[Money alloc] init:amount];
        [theMoney autorelease]; // We are responsible for sending it release
        return theMoney;
    } 
    // Caller
    Money *myMoney = [bank showMeTheMoney:4500.00];
    [myMoney retain];

Collections and autorelease
Loading up an array or dictionary to return to a caller
    @implementation MyObject
    - (NSArray *)coolCats
    {
        NSMutableArray *returnValue = [[NSMutableArray alloc] init];
        [returnValue addObject:@"Steve"];
        [returnValue addObject:@"Ankush"];
        [returnValue addObject:@"Sean"];
        [returnValue autorelease];
        return returnValue;
    }
    @end

    // But there's a better way. Get an autoreleased NSMutableArray in the first place.
    @implementation MyObject
    - (NSArray *)coolCats
    {
        NSMutableArray *returnValue = [[NSMutableArray alloc] init];
        NSMutableArray *returnValue = [NSMutableArray array];
        [returnValue addObject:@"Steve"];
        [returnValue addObject:@"Ankush"];
        [returnValue addObject:@"Sean"];
        [returnValue autorelease];
        return returnValue;
    }
    @end

   // But there's an even better way! Use collection classes "create with" methods.
    @implementation MyObject
    - (NSArray *)coolCats
    {
        NSMutableArray *returnValue = [NSMutableArray array];
        [returnValue addObject:@"Steve"];
        [returnValue addObject:@"Ankush"];
        [returnValue addObject:@"Sean"];
        return returnValue;
       
        return [NSArray arrayWithObjects:@"Steve", @"Ankush", @"Sean", nil];
    }
    @end  

Other convenient create with methods (all return autoreleased objects):
1. [NSString stringWithFormat:@"Meaning of %@ is %d", @"life", 42];
2. [NSDictionary dictionaryWithObjectsAndKeys:ankush, @"TA", janestudent,
   @"Student", nil];

3. [NSArray arrayWithContentsOfFile:(NSString *)path];

Other Ownership Rules
Collections take ownership when an object is added to them
    NSArray, NSDictionary, NSSet
    (NSDictionary takes ownership of both keys and values).
    They then release ownership when an object is removed.

Think of @"string" as autoreleased
    In reality, they are constants, so retain and release have no effect on them.

NSString objects are usually sent copy rather than retain (會產生新物件)
    1. That gives you an immutable version of the string to hold on to.
    2. The method copy in NSMutableString returns an NSString,
       not an NSMutableString.
    3. Of course, you still release it when you are done with it.

Deallocation
What happens when the last owner calls release?
    A special method, dealloc, is called on the object
    & the object's memory is returned to the heap.
    After this happens, sending a message to the object will crash your program.

You should override dealloc in your classes, but NEVER call it!
    1. It only gets called by release when the last owner has released the object.
    2. The one exception about calling it is that you must call [super dealloc]
       in your dealloc.

Example
    - (void)dealloc
    {
        [brain release];
        [otherObjectInstanceVariable release];
        [super dealloc];
    }


@property
There are three options for setters made by @synthesize
    1. @property (retain) NSArray *myArrayProperty;      // retain count + 1
    2. @property (copy) NSString *someNameProperty;  //
retain count + 1
    3. @property (assign) id delegate;                            // retain count + 1
 
Example for retain:
    @property (retain) NSString *name;
    @synthesize
    // will create a setter equivalent to this ...
   
    - (void)setName:(NSString *)aString
    {
        [name release];
        name = [aString retain];
    }
    // Note that @synthesize will release the previous object
    // (if any, could be nil) before setting and retaining the new one.

Example for copy:
    @property (copy) NSString *name;
    @synthesize
    // will create a setter equivalent to this ...
   
    - (void)setName:(NSString *)aString
    {
        [name release];          // Still release-ing before copying.
        name = [aString copy];
    }


Example for assign:
    @property (assign) NSString *name;
    @synthesize
    // will create a setter equivalent to this ...
    - (void)setName:(NSString *)aString
    {
        name = aString; // No release here because we never retain or copy.
    }

-----------------------------------------------------------------------------------------------------

Protocols

Similar to @interface, but no implementation
    @protocol Foo
    - (void)doSomething;    // implementors must implement this
    @optional
    - (int)getSomething;    // implementors do not need to implement this
    @required
    - (NSArray *)getManySomethings:(int)howMany;    // must implement
    @end
    // The above is added to a header file


Classes then implement it
    // They must proclaim that they implement it in their @interface
    @interface MyClass : NSObject <Foo>
    ....
    @end

 
You must implement all non-@optional methods

Can now declare id variables with added protocol requirement
    id<Foo> obj = [[MyClass alloc] init]; //多型: compiler will love this!


Also can declare arguments to methods to require protocol
    - (void)giveMeFooObject:(id <Foo>)anObjectImplementingFoo;
    // If you call this and pass an object which does not implement Foo
    // ... compiler warning!


Number one use of protocols in iOS: delegates and dataSources
    The delegate or dataSource is always defined as an assign @property
    @property (assign) id <UISomeObjectDelegate> delegate;

-----------------------------------------------------------------------------------------------------

Views
Hierarchical
    1. Only one superview - (UIView *)superview
    2. Can have many (or zero) subviews - (NSArray *)subviews
    3. Subview order (in that array) matters:
       those later in the array are on top of those earlier

 
it can be done in code as well
    - (void)addSubview:(UIView *)aView;
    - (void)removeFromSuperview;
View Transparency(透明度)
    1. By default, drawing is fully opaque(預設: 不透明)
    2. you can hide a view completely by setting hidden property
       @property BOOL hidden;
       myView.hidden = YES;    // view will not be on screen and will not handle events

       (不畫)
 
View Memory Management
    1. A superview retains its subviews
       Once you put a view into the view hierarchy, you can release your ownership 

       if you want
    2. Be careful when you remove a view from the hierarchy
       (1). If you want to keep using a view, retain ownership before you send 

           removeFromSuperview
       (2). Removing a view from the hierarchy immediately causes a release on it 

           (not autorelease)
       (3). So if there are no other owners, it will be immediately deallocate

           (and its subviews released)    
    3. IBOutlets are retained
-----------------------------------------------------------------------------------------------------

IBOutlet Memory Management


UIViewController calls a method on itself after view load/unload
   
- (void)viewDidLoad is called just after the Controller's view has been created
      (& outlets are set). This method is an awesome place to set initial state 

      in an outlet (if you couldn't do it in IB).

    - (void)viewDidUnload is called just after the view has been "unloaded"
    This is the place we can release our outlets in the case of unloading.


Here's how we release our outlets in viewDidUnload & dealloc
    // private method to share code between viewDidUnload and dealloc

    - (void)releaseOutlets {
        self.myOutlet = nil;
        self.myOtherOutlet=nil;
    }
    - (void)viewDidUnload {
        [self releaseOutlets];
    }

    - (void)dealloc {
        [self releaseOutlets];
        [super dealloc];
    }

-----------------------------------------------------------------------------------------------------

Coordinates
Origin of a view's coordinate system is upper left 

Units are "points" (not pixels)

    @property CGFloat contentScaleFactor;
    // returns pixels per point on the screen this view is on.
    // This property is not (readonly), but you should basically pretend that it is for this class.



Views have 3 properties related to their location and size
    1. @property CGRect bounds;  

       // your view's internal drawing space's origin and size
       The bounds property is what you use inside your view's own implementation
       It is up to your implementation as to how to interpret bounds.origin

 
    2. @property CGPoint center;  

       // the center of your view in your superview's coordinate space
 
    3. @property CGRect frame;  

       // a rectangle in your superview's coordinate space which entirely
       // contains your view's bounds.size

 
-----------------------------------------------------------------------------------------------------

Creating Views

How do you create a UIView in code (i.e. not in IB)?
    Just use alloc and initWithFrame: (UIView's designated initializer).

Example:
    CGRect buttonRect = CGRectMake(20, 20, 120, 37);
    UIButton *button = [[UIButton alloc] initWithFrame:buttonRect];
    button.titleLabel.text = @"Do it!";
    [window addSubview:button];
    [button release];    // okay because button is in view hierarchy now

 
-----------------------------------------------------------------------------------------------------

Custom Views

Drawing is easy ... create a UIView subclass & override 1 method
    - (void)drawRect:(CGRect)aRect; // 會使用 CGCore Graphic, CPU 畫圖效能好
    You can optimize by not drawing outside of aRect if you want (but not required).

NEVER call drawRect:!! EVER! Or else!
   Instead, let iOS know that your view's drawing is out of date with one of
   these UIView methods:
   - (void)setNeedsDisplay;
   - (void)setNeedsDisplayInRect:(CGRect)aRect;
   It will then set everything up and call drawRect: for you at an appropriate time
   Obviously, the second version will call your drawRect: with only rectangles that
   need updates

So how do I implement my drawRect:?
    Use the Core Graphics framework
    The API is C (not object-oriented)


-----------------------------------------------------------------------------------------------------

Context
How to get this magic context?
    Call the following C function inside your drawRect: method to get 

    the current graphics context ...
    CGContextRef context = UIGraphicsGetCurrentContext();
    You do not have to free it or anything later, just use it for all drawing.


-----------------------------------------------------------------------------------------------------

Graphics State
Special considerations for defining drawing "subroutines"
    - (void)drawGreenCircle:(CGContextRef)ctxt {
        UIGraphicsPushContext(ctxt);
        [[UIColor greenColor] setFill];
        // draw my circle
        UIGraphicsPopContext();
    }

    - (void)drawRect:(CGRect)aRect {
        CGContextRef context = UIGraphicsGetCurrentContext();
        [[UIColor redColor] setFill];
        // do some stuff
        [self drawGreenCircle:context];
        // do more stuff and expect fill color to be red
    }

-----------------------------------------------------------------------------------------------------

Drawing Images
Use UIImageView to draw images, but if you feel you must ...

Create a UIImage object from a file in your Resources folder
    UIImage *image = [UIImage imageNamed:@"foo.jpg"];

Or create one from a named file or from raw data
    UIImage *image = [[UIImage alloc] initWithContentsOfFile:(NSString *)fullPath];
    UIImage *image = [[UIImage alloc] initWithData:(NSData *)imageData];

 
Or you can even create one by drawing with CGContext functions
    UIGraphicsBeginImageContext(CGSize);
    // draw with CGContext functions
    UIImage *myImage = UIGraphicsGetImageFromCurrentContext(); // screenshot
    UIGraphicsEndImageContext();

Now blast the UIImage's bits into the current graphics context

    UIImage *image = ...;
    [image drawAtPoint:(CGPoint)p];    // p is upper left corner of the image
    [image drawInRect:(CGRect)r];      // scales the image to fit in r
    [imagedrawAsPatternInRect:(CGRect)patRect; //tiles the image into patRect


Aside: You can get a PNG or JPG data representation of UIImage
    NSData *jpgData = 
    UIImageJPEGRepresentation((UIImage *)myImage, (CGFloat)quality);
 
    NSData *pngData = 

    UIImagePNGRepresentation((UIImage *)myImage);

2011年6月13日 星期一

Stanford CS193P Note-02

id: a pointer to any kind of object.

◎ MVC 小記: @class 的使用
   假設有一個 Model: CalculatorBrain class
   1. 在 CalculatorViewController.h 中只需引入 class 名稱時:
      @class CalculatorBrain;
   2. 在 CalculatorViewController.m 中才真正會使用到此 class:
      #import "CalculatorBrain.h"

Class Methods
    1. ex:
       + (id)alloc;   
      // makes space for an object of the receiver's class (always pair w/init)

    2. Can not access instance variables inside

Instance Variables
    Scope: Instance Variables By default, instance variables are @protected
            (only the class and subclasses can access).

------------------------------------------------------------------------------------------------------

Properties
    1. Mark all of your instance variables @private.
    2. Use @property and "dot notation" to access instance variables.
       a. You can get the compiler to generate set/get method declarations with
          @property directive
       b. If you use the readonly keyword, only the getter will be declared
          @property (readonly) int eye;   
          // does not declare a setEye: method
    3. Create methods to set/get an instance variable's value
    4. Let the compiler help you with implementation using @synthesize!

    5. 建議的作法:
// Header (.h) file:
@interface MyObject : NSObject
{
@private
    int p_eye; // private variable
}
@property int eye;
@end

// Implementation (.m) file:
@implementation MyObject
@synthesize eye = p_eye;
/*
- (int)eye {
    return p_eye;
}
- (void)setEye:(int)anInt {
    p_eye = anInt;
}
*/
@end

    6. Notice:
       access to instance variable using property of self instead of directly.

------------------------------------------------------------------------------------------------------

Private Properties
    You can declare a "private interface" to your class inside your implementation file.
    Example (this is all in MyObject's.m file):

/*
This is the "magic" to declare your private stuff. You can put properties and methods here, but not more instance variables.
*/
@interface MyObject()
@property double myEyesOnly;
@end

@implementation MyObject
@synthesize eye, myEyesOnly;
@end

=> The property myEyesOnly can only be set/get via self.myEyesOnly
    since it is private.

------------------------------------------------------------------------------------------------------

Introspection
    1. All objects that inherit from NSObject know these methods:
       a. isKindOfClass: returns whether an object is that kind of class
                             (inheritance included)
       b. isMemberOfClass: returns whether an object is that kind of class
                                 (no inheritance)
       c. respondsToSelector: returns whether an object responds to a given method

    2. You get a Class by sending the class method class to a class :)
       ex:
if ([obj isKindOfClass:[NSString class]]) {
    NSString *s = [(NSString *)obj stringByAppendingString:@”xyzzy”];
}

    3. Method testing methods take a selector (SEL)
       Special @selector() directive turns the name of a method into a selector
       ex:
if ([obj respondsToSelector:@selector(shoot)]) {
    [obj shoot];
}

    4. SEL is the Objective-C "type" for a selector
SEL shootSelector = @selector(shoot);
SEL moveToSelector = @selector(moveTo:);

Target/action uses this, e.g.
[button addTarget:self action:@selector(digitPressed:)]

    5. If you have a SEL, you can ask an object to perform it
       Using the performSelector: or performSelector:withObject: methods in NSObject
       ex:
[obj performSelector:shootSelector];
[obj performSelector:moveToSelector withObject:coordinate];

------------------------------------------------------------------------------------------------------

nil
   1. The value of an object pointer that does not point to anything
   2. NSObject sets all its instance variables to zero
      Thus, instance variables that are pointers to objects start out with the value of nil.
   3. Can be implicitly tested in an if statement
      ex: if(obj){} //curly braces will execute if obj points to an object
   4. Sending messages to nil is (mostly) okay. No code gets executed.
      ex:
      a. If the method returns a value, it will return zero.
         int i = [obj methodWhichReturnsAnInt]; // i will be zero if obj is nil
      b. Be careful if the method returns a C struct. Return value is undefined.
         CGPoint p = [obj getLocation]; //p will have an undefined value if obj is nil

------------------------------------------------------------------------------------------------------

BOOL
    1. Objective-C's boolean "type" (actually just a typedef)
    2. YES means "true," NO means "false"
    3. NO == 0, YES is anything else

2011年6月12日 星期日

Stanford CS193P Note-01

Design Strategies (設計策略): MVC

1. Divide(劃分) objects in your program into 3 "camps(營地)."
(1). Model = What your application is (but not how it is displayed)
(2). Controller = How your Model is presented(呈現) to the user (UI logic)
(3). View = Your Controller's minions(手下)


2. It's all about managing communication between camps

(1). Controllers can always talk directly to their Model.
(2). Controllers can also talk directly to their View.
(3). The Model and View should never speak to each other.


Controllers and View:
3. Can the View speak to its Controller?
   (1). Sort of. Communication is "blind" and structured.
   (2). The Controller can drop a target on itself.
   (3). Then hand out an action to the View.
   (4). The View sends the action when things happen in the UI.

4. Sometimes the View needs to synchronize with the Controller.
   (1). The Controller sets itself as the View's delegate.
   (2). The delegate is set via a protocol (i.e. it's "blind" to class). 

5. Views do not own the data they display.
   (1). So, if needed, they have a protocol to acquire(獲得) it.
   (2). Controllers are almost always that data source (not Model!).
   (3). Controllers interpret/format(詮釋/格式化) Model information for the View.


Controllers and Model:


6. Can the Model talk directly to the Controller?
(1). No. The Model is (should be) UI independent.
(2). So what if the Model has information to update or something?
(3). It uses a "radio station"-like broadcast mechanism(機制).
(4). Controllers (or other Model) "tune in"(收聽) to interesting stuff.
(5). A View might "tune in," but probably not to a Model's "station."


7. Now combine MVC groups to make complicated programs ...