iOS-Foundation and Attribute Strings

Extension of iOS-Objective-C and Java. Dive into iOS Foundation and Attribute Strings.

Creating Objects

  • create objects with alloc and init

    1
    NSMutableArray *cards = [[NSMutableArray alloc] init];
    CardMatchingGame *game = [[CardMatchingGame alloc] initWithCardCount:12 usingDeck:d];
  • create with class methods

    1
    NSString’s + (id)stringWithFormat:(NSString *)format, ... NSString *moltuae = [NSString stringWithFormat:@“%d”, 42];
  • Sometimes both a class creator method and init method exist

    1
    [NSString stringWithFormat:...] same as [[NSString alloc] initWithFormat:...]

    iOS seems to be moving more toward the alloc/init versions with new API, but is mostly neutral.

  • ask other objects to create new objects

    1
    2
    3
    NSString’s - (NSString *)stringByAppendingString:(NSString *)otherString;
    NSArray’s - (NSString *)componentsJoinedByString:(NSString *)separator;
    NSString’s & NSArray’s - (id)mutableCopy;

    But not all objects given out by other objects are newly created, NSArray’s - (id)lastObject; NSArray’s - (id)objectAtIndex:(int)index;unless the method has the word “copy” in it, if the object already exists, you get a pointer to it. If the object does not already exist (like the first 2 examples at the top), then you’re creating.

Dynamic Binding

Objective-C has an important type called id.

It means “pointer to an object of unknown/unspecified” type. id myObject; Really all object pointers (e.g. NSString *) are treated like id at runtime. But at compile time, if you type something NSString * instead of id, the compiler can help you. It can find bugs and suggest what methods would be appropriate to send to it, etc.
If you type something using id, the compiler can’t help very much because it doesn’t know much. Figuring out the code to execute when a message is sent at runtime is called “dynamic binding.”

static typing

1
NSString *s = @“x”; // "statically" typed
id obj = s; // legal, Never use “id *” by the way
NSArray *a = obj; // also legal but dangerous

When to use intentionally

  • When we want to mix objects of different classes in a collection (e.g. in an NSArray).
  • When we want to support the “blind, structured” communication in MVC (i.e. delegation). And there are other generic or blind communication needs.
  • But to make these things safer, we’re going to use two things: Introspection and Protocols.

Introspection

Asking at runtime what class an object is or what messages can be sent to it.
All objects that inherit from NSObject know these methods:

1
2
3
isKindOfClass: returns whether an object is that kind of class (inheritance included) 
isMemberOfClass: returns whether an object is that kind of class (no inheritance)
respondsToSelector: returns whether an object responds to a given method

Protocol

A syntax that is “in between” id and static typing.
Does not specify the class of an object pointed to, but does specify what methods it implements. Example …

1
id <UIScrollViewDelegate> scrollViewDelegate;

A little bit like template in C++ and Java.

Arguments to these methods

  • Class testing methods take a Class
1
if ([obj isKindOfClass:[NSString class]]) {
   	NSString *s = [(NSString *)objstringByAppendingString:@”xyzzy”];
}

Method testing methods take a selector (SEL)

1
2
// Special @selector() directive turns the name of a method into a selector
if ([obj respondsToSelector:@selector(shoot)]) { [obj shoot]; } else if ([obj respondsToSelector:@selector(shootAt:)]) { [obj shootAt:target]; } //otherwise, nothing happens

ask an object to perform SEL

1
2
3
// Using the performSelector: or performSelector:withObject: methods in NSObject
[obj performSelector:shootSelector]; [obj performSelector:shootAtSelector withObject:coordinate]; // Using makeObjectsPerformSelector: methods in NSArray
[array makeObjectsPerformSelector:shootSelector]; // [array makeObjectsPerformSelector:shootAtSelector withObject:target];

NSObject

Base class for pretty much every object in the iOS SDK.

- (NSString *)description is a useful method to override (it’s %@ in NSLog()). Example … NSLog(@“array contents are %@”, myArray);
The %@ is replaced with the results of invoking [myArray description].

Copying objects. This is an important concept to understand (why mutable vs. immutable?).

- (id)copy; // not all objects implement mechanism (raises exception if not)
- (id)mutableCopy; // not all objects implement mechanism (raises exception if not)
1
2
3
4
5
6
7
8
9
10
11
12
13

## NSArray

Ordered collection of objects. Immutable. That’s right, once you create the array, you cannot add or remove objects.
All objects in the array are held onto strongly. Usually created by manipulating other arrays or with `@[]`.
```
- (NSUInteger)count; - (id)objectAtIndex:(NSUInteger)index; // crashes if index is out of bounds; returns id!
- (id)lastObject; // returns nil (doesn’t crash) if there are no objects in the array - (id)firstObject; // returns nil (doesn’t crash) if there are no objects in the array - (NSArray *)sortedArrayUsingSelector:(SEL)aSelector; - (void)makeObjectsPerformSelector:(SEL)aSelector withObject: (id)selectorArgument; - (NSString *)componentsJoinedByString:(NSString *)separator; ```

## NSMutableArray

Mutable version of NSArray. Create with alloc/init or ...
``` + (id)arrayWithCapacity:(NSUInteger)numItems; // numItems is a performance hint only + (id)array; // [NSMutableArray array] is just like [[NSMutableArray alloc] init]
NSMutableArray inherits all of NSArray’s methods. Not just count, objectAtIndex:, etc., but also the more interesting ones mentioned above. Other key methods:
1
- (void)addObject:(id)object; // to the end of the array (note id is the type!) - (void)insertObject:(id)object atIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
## Enumeration - Looping through members of an array in an efficient manner
1
Example: NSArray of id NSArray *myArray = ...; for (id obj in myArray) {
// 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 no worries }
}
## Foundation Framework ### NSNumber Object wrapper around primitive types like int, float, double, BOOL, enums, etc.
1
NSNumber *n = [NSNumber numberWithInt:36];
float f = [n floatValue]; // would return 36.0 as a float (i.e. will convert types)
// Useful when you want to put these primitive types in a collection (e.g. NSArray or NSDictionary).

NSNumber *three = @3;
NSNumber *underline = @(NSUnderlineStyleSingle); // enum
NSNumber *match = @([card match:@[otherCard]]); // expression that returns a primitive type

1
2
3
4
5

### NSValue

Generic object wrapper for some non-object, non-primitive data types (i.e. C structs).

NSValue *edgeInsetsObject = [NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1,1,1,1)]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

## Property List

The term “Property List” just means a collection of collections It’s just a phrase (not a language thing). It means any graph of objects containing only: - `NSArray`, `NSDictionary`, `NSNumber`, `NSString`, `NSDate`, `NSData` (or mutable subclasses thereof)
An NSArray is a Property List if all its members are too - So an `NSArray` of `NSString` is a Property List. - So is an `NSArray` of `NSArray` as long as those NSArray’s members are *Property Lists*. An `NSDictionary` is one only if all keys and values are too An `NSArray` of `NSDictionarys` whose keys are NSStrings and values are NSNumbers is one.
### Why define this type?
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.

## Attribute Strings

Attribute Strings decide How text looks on screen.

### Getting Attribute

You can ask an NSAttributedString all about the attributes at a given location in the string.

  • (NSDictionary *)attributesAtIndex:(NSUInteger)index
    effectiveRange:(NSRangePointer)range;
    1
    2
    3
    4
    5

    ### NSMutableAttributedString

    - add an attribute to a range of characters

  • (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range;
    // which will change the values of attributes in attributes and not touch other attributes. Or you can set the attributes in a range
  • (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range;
    // which will remove all other attributes in that range in favor of the passed attributes. You can also remove a specific attribute from a range
  • (void)removeAttribute:(NSString *)attributeName range:(NSRange)range;
    1
    2
    3
    4
    5
    6
    7

    - Modifying the contents of the string (changing the characters)

    do that with methods to append, insert, delete or replace characters. Or call the `NSMutableAttributedString` method `- (NSMutableString *)` mutableString and modify the returned NSMutableString (attributes will, **incredibly, be preserved**!)

    ### a set of attributes

    @{ NSFontAttributeName :
    [UIFont preferredFontWithTextStyle:UIFontTextStyleHeadline]
    NSForegroundColorAttributeName : [UIColor greenColor],
    NSStrokeWidthAttributeName : @-5,
    NSStrokeColorAttributeName : [UIColor redColor] }
    ```

Reference

Online Course: Developing iOS7 Apps for iPhone and iPad, Stanford