这节课主要讲了Objecitive-C的几个细节和Foundation库中的一些类
本节课主要介绍iOS开发
- NSObject
- NSArray
- NSMutableArray
- NSNumber
- NSValue
- NSData
- NSDate
- NSSet
- NSMutableSet
- NSOrderedSet
- NSMutableOrderedSet
- NSDictionary
- NSMutableDictionary
- NSUserDefaults
- NSRange
- UIColor
- UIFont
- UIFontDescriptor
- NSAttributedString
- NSMutableAttributedString
Objecitive-C的几个细节
对象的创建
- 利用Alloc & init创建对象
- 利用类的方法创建对象
- 或者上述两者同时使用
- 让其他对象帮忙创建类的对象1 
 2
 3- NSString's -- (NSString *)stringByAppendingString:(NSString *)otherString 
 - NSArray's -- (NSString *)componentsJoinedByString:(NSString *)separator;
 - NSString's & NSArray's -- (id)mutableCopy
nil
向nil发送消息是可以的,但是不会执行任何代码
动态绑定
Objective-C有个非常重要的类型——id,id是一种类型,意味“指向未知类型对象的指针”。
在运行过程中,所有对象的指针都是id类型的。
用id来指定变量类型,就是动态绑定。
但是在编译过程中,如果你指定对象的类型,编译器可以帮助发现bug。
那么动态绑定安全么?
动态绑定导致可以向对象发送未定义的信息,比如该对象类型为定义的方法,当然这会导致程序崩溃。
如果在代码中指定对象的类型,这种错误就可以在编译过程中被发现,防止程序在执行过程中崩溃,比如,NSString *s  = @'x',这种情况下,如果给s发送非NSString的方法,编译器可以发现错误。
当然也可以使用:id obj = s、NSArray *a = obj,但是这样编译器没办法帮忙检查错误,所以存在一定危险。
另外,永远不要使用id *,因为id本身就是个指针。
| 1 | @interface Vehicle | 
| 1 | Ship *s = [[Ship alloc] init]; | 
什么时候会用到危险的动态绑定(id)
- Objective-C允许在同一个collection(比如NSArray)中有不同类型的类,想这么做时,需要用到动态绑定。
- 当希望使用MVC中的blind/structured通信(比如delegation)时。
为了把动态绑定变得安全,需要使用Introspection和Protocols
- Introspection - 在运行时询问id是什么类型或询问可以向其发送的信息。 - 继承自NSObject的所有对象都有下面三个函数: - isKindOfClass:- 判断对象是不是某种类,包含继承的情况 - 1 
 2
 3- if([obj isKindOfClass:[NSString class]]) { 
 NSString *s = [(NSString*)obj stringByAppendingString:@"xyzzy"];
 }- 比如在纸牌匹配游戏中 - PlayingCard.m的- match函数的源代码为:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- - (int)match:(NSArray *)otherCards 
 {
 int score = 0;
 if([otherCards count] == 1) {
 PlayingCard *otherCard = [otherCards firstObject];
 if([self.suit isEqualToString:otherCard.suit]) {
 score = 1;
 } else if (self.rank == otherCard.rank) {
 score = 4;
 }
 }
 return score;
 }- 可以改为: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16- - (int)match:(NSArray *)otherCards 
 {
 int score = 0;
 if([otherCards count] == 1) {
 id card = [otherCards firstObject];
 if([card isKindOfClass:[PlayingCard class]]) {
 PlayingCard *otherCard = (PlayingCard *)card;
 if([self.suit isEqualToString:otherCard.suit]) {
 score = 1;
 } else if (self.rank == otherCard.rank) {
 score = 4;
 }
 }
 }
 return score;
 }
- isMemberOfClass:- 判断对象是不是某种类,不包括继承的情况 
- respondsToSelector:- 判断对象能否相应某个函数 - 1 
 2
 3
 4
 5- if([obj respondsToSelector:@selector(shoot)]) { 
 [obj shoot];
 } else if ([obj respondsToSelector:@selector(shootAt:)]) {
 [obj shootAt:target];
 }
 - 在objective-C中,SEL是selector的一种类型 - 1 
 2
 3- SEL shootSelector = @selector(shoot); 
 SEL shootAtSelector = @selector(shootAt:);
 SEL moveToSelelctor = @selector(moveTo:withPenColor);
- 给定SEL,可以要求对象执行selector - 1 
 2- [obj performSelector:shootSelector]; 
 [obj performSelector:shootAtSelector withObject:coordinate];- 1 
 2
 3
 4- [array makeObjectsPerformSelector:shootSelector]; 
 // cool, huh?
 [array makeObjectsPerformSelector:shootAtSelector withObject:target];
 // target is an id
 
- Protocols - 不指定指针指向的对象类型,但是指定其实现的函数。 
Foundation库
NSObject
在iOS SDK中 ,NSObject几乎是所有类的基类。
NSObject包含一个非常有用的函数- (NSString *)description,会返回对类的描述,所以最好在自己实现的类中对其重写。该函数一般用在两个地方:
- 用NSLog打印出来, - NSLog(@"array contents are %@", myArray);,用于debug
- 并不是所有的对象都实现了 - - (id)copy和- - (id)mutableCopy,在未实现它们的对象中调用,会抛出异常
- 你向可修改对象(比如MutableArray)发送copy,返回值并不是可修改对象,而是获得一个不可修改的对象。 
NSArray
NSArray是对象的有序集合,不可修改(immutable),一旦创建,不能添加或删除对象。
只要NSArray本身在内存的堆中,其中所有的对象都有strong指针指向它们。
一般通过@[]手动创建NSArray,NSArray中包括以下一些关键函数:
| 1 | - (NSUInteger)count; | 
NSMutableArray
是NSArray的可变版本(mutable),继承了NSArray的所有函数
一般通过alloc/init创建,或者通过以下函数创建
| 1 | + (id)arrayWithCapacity:(NSUInteger)numItems; | 
对于NSArray和NSMutableArray两种数组,可以用for-in的方式遍历:
Objective-C支持for-in形式的数组遍历。需要注意的是,for-in形式的数组遍历默认会进行强制类型转换,比如:
| 1 | NSArray *myArray = ...;// 任意类型数组 | 
那么如果数组中的数据类型不同,如何进行遍历呢?可以使用id
| 1 | NSArray *myArray = ...;// 同样,任意类型的数组 | 
NSNumber
NSNumber是对元类型int、float、double、BOOL、enum等的封装。当你想把这些元类型的数据放入同一个数组时很有用。
NSValue
类似于NSNumber,但是用来封装一些非对象、非元类型的数据,例如c structs。
NSData
用于存储bits
NSDate
用于存储日期,相关的类型还有NSCalendar,NSDateFormatter,NSDateComponents。
NSSet/NSMutableSet
NSOrderedSet/NSMutableOrderedSet
NSDictionary
存储键值对,不可修改。
NSDictionary可以用@{key1:value1, key2:value2}创建,比如:
| 1 | NSDictionary *colors = @{ @"green" : [UIColor greenColor], | 
NSMutableDictionary
NSDictionary的可修改版本,出了继承了NSDictionary所有的函数,还有下面几个重要函数:
| 1 | - (void)setObject:(id)anObject forKey:(id)key; | 
Dictionary和NSMutableDictionary可用如下方法遍历:
| 1 | NSDictionary *myDictionary = ...; | 
NSUserDefaults
介绍NSUserDefaults之前,需要先介绍一个术语:Property List
Property List是一个iOS开发中常用术语,在一些API中会用到,比如:
| 1 | - (void)writeToFile:(NSString *)path atomically:(BOOL)atom; | 
这个函数只能发送给NSArray或NSDictionary,并且其中只能包含Property List对象。
- 概念: - Property List可以理解为只包含NSArray、NSDictionary、NSNumber、NSString、NSDate、NSDate的一个集合,并且Property List是一个递归的概念。 - 比如一个由NSString组成的NSArray,就是一个Property List - 一个由NSArray组成的NSArray是不是一个Property List要看子NSArray是不是Property List,如果是,那组成的NSArray就是一个Property List,否则不是。 - 对于NSDictionary,只有当其所有key和value都是Property List的时候才是Property List - 那么,假设有一个NSArray,其中包含了多个NSDictionary,这些NSDictionary的key都是NSString,value都是NSNumber,那么这个NSArray也是个Property List 
NSUserDefaults就是Property List的轻量级存储,可以看成一个持久存储的NSDictionary。算不上一个数据库,通常只用来存储小数据,比如用户设置。
利用standardUserDefaults读写:
| 1 | [[NSUserDefaults standardUserDefaults] setArray:rvArray forKey:@"RecentlyViewed"]; | 
| 1 | - (void)setDouble:(double)aDouble forKey:(NSString *)key; | 
更改后一定要使用synchronize进行同步,才能写入永久内存。
| 1 | [[NSUser Defaults standardUserDefaults] synchronize]; | 
NSRange
是一个类似C语言中struct的结构,用于指定string和数组的界限,其结构如下:
| 1 | typedef struct { | 
使用方法如下:
| 1 | NSString *greeting = @"hello world"; | 
UIColor
一个非常简单的类,用于表示颜色。可以用RGB或者HSB等方法初始化。
UIFont
字体在UI设计中非常重要,iOS在不同截面显示的字体变化很大,所以要重视UIFont这个类。
对于展示内容而言,使用Font最好的方法是调用preferredFontForTextStyle
| 1 | UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; | 
除了UIFontTextStyleBody,还有UIFontTextStyleHeadline、UIFontTextStyleCaption1、UIFontTextStyleFootnote等等。
对于按键内容等,可以使用系统字体:
| 1 | + (UIFont *)systemFontOfSize:(CGFloat)pointSize; | 
不要将系统字体用于内容。
UIFontDescriptor
字体是由艺术家设计的,并没有特定的准则,有些字体甚至没有加黑。尽管如此,UIFontDescriptor尝试对所有字体进行分类。
NSAttributedString
iOS开发中经常需要显示一些带有特殊样式的文本,比如说带有下划线、删除线、斜体、空心字体、背景色、阴影以及图文混排(一种文字中夹杂图片的显示效果)。
通常想要实现这些效果要使用到iOS的Foundation框架提供的NSAttributedString类,NSAttributedString类中有许多属性,不同属性对应不同的文本样式。本文主要对这些属性做一个解释说明,并会结合实际代码来应用它们。
可以将NSAttributedString想象成NSString,其每个字母有个叫做attributes的NSDictionary。
NSAttributedString不是NSString的继承,所以不能使用NSString的函数。
NSMutableAttributedString
相对于NSAttributedString,NSMutableAttributedString更加常用。
添加或者设置字母对应attributes的方法:
| 1 | - (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range; | 
| 1 | UIColor *yellow = [UIColor yellowColor]; | 

attributed strings用在哪里?
| 1 | // UIButton |