这节课主要讲了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
3if([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
5if([obj respondsToSelector:@selector(shoot)]) {
[obj shoot];
} else if ([obj respondsToSelector:@selector(shootAt:)]) {
[obj shootAt:target];
}
在objective-C中,SEL是selector的一种类型
1
2
3SEL 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 |