创建对象
多数时候,我们创建对象以alloc和init...
NSMutableArray *card = [[NSMutableArray alloc] init];
CardMatchingGame *game = [[CardMatchingGame alloc] initWithCardCount:12 usingDeck:d];
或者以类方法
NSString:
+ (instancetype)stringWithFormat:(NSString *)format, ...
NSString *moltuae = [NSString stringWithFormat:@"%d", 42];
UIButton:
+ (id)buttonWithType:(UIButtonType)buttonType;
NSMutableArray:
+ (instancetype)arrayWithCapacity:(NSUInteger)numItems;
NSArray:
+ (instancetype)arrayWithObject:(id)anObject;
有时类创建方法与init方法共存
[NSString stringWithFormat:...]
等同
[[NSString alloc] initWithFormat:...]
不要被这个干扰。使用任一版本皆可。
iOS似乎倾向分配/初始化版本在新的API,但大多是中性的。
你也可以要求其他对象来为你创建新的对象
NSString:
- (NSString *)stringByAppendingString:(NSString *)aString;
NSArray:
- (NSString *)componentsJoinedByString:(NSString *)separator;
NSString与NSArray:
- (id)mutableCopy;
但是,并非其他对象给出的对象都是新创建的
NSArray:
- (id)lastObject;
NSArray:
- (id)objectAtIndex:(NSUInteger)index;
除在它的方法有“copy”这个单词,如果对象已经存在,你将得到一个指向它的指针
如果对象不是已经存在的,这时创建
nil
给nil发消息(通常)是OK的。没有代码会执行。
如果该方法返回一个值,它会返回零。
int i = [obj methodWhichReturnsAnInt]; // 如果obj是nil,i将会是零
依靠这个并且
编写代码
使用这个
是绝对没问题的
但要小心,如果该方法返回一个C结构。返回值是不确定的。
CGPoint p = [obj getLocation]; // 如果obj是nil,p将有一个不确定的值
动态绑定
Objective-C有个重要的类型叫做id
意思是“指向一个未知/不确定的对象”类型
id myObject;
真正的所有的对象指针(如的NSString*)在运行时将像id一样对待
但是在编译的时候,如果你输入NSString*的东西,而不是id的,编译器可以帮助你
它可以发现bug,并建议用什么方法将是适合发给它的,等
如果你使用的id类型的东西,编译器不会有很大的帮助,因为它知道的并不多
弄清楚当消息在运行时发送,代码的执行被称为“动态绑定”
它安全么?
对待所有对象指针为“指向未知类型的”在运行时看似危险,对不对?
是什么阻止你发送消息到它不明白的一个对象?
什么都没有。并且如果你这样做你的程序会崩溃。天哪,Objective-C程序必须崩溃很多
了!
不尽然
因为我们大多使用静态类型(如NSString*)并且编译器真的是很聪明
静态类型
NSString* s =@“x”; //“静态”类型(如果s发送非NSString消息,编译器会发出警告)
id obj = s; //不是静态类型,但是完全合法;编译器不能捕获[obj rank]
NSArray *a = obj; //同样合法,但很明显会导致一些大麻烦!
编译器不会抱怨id和静态类型变量之间的分配
有时候,你正在默默地这样做。你已经这样做了!
- (int)match:(NSArray *)otherCards
{
...
PlayingCard *otherCard = [otherCards firstObject]; //firstObject返回id!
...
}
切勿使用“id *”的方式(这将意味着“一个指向对象的指针的指针”)
我们什么时候故意地使用这个危险的情况呢!
当我们想把不同类的对象混在同一个集合的时候(例如在一个NSArray)
当我们想在MVC中支持的“盲的,结构化”的通信(即委托)
而且,还有其它通用或盲的通信需求
但是为了让这些更安全,我们将使用两件事:内省和协议
内省
在运行时询问对象是什么类或者可以发送什么消息给它
协议
语法是“介于两者之间”id和静态类型
没有指定对象指向的类,但是指定了什么方法是它实现的
例如...
id <UIScrollViewDelegate> scrollViewDelegate;
我们将在下周讨论如何声明和使用协议
内省
所有继承自NSObject类的对象知道这些方法...
isKindOfClass: 返回BOOL:一个对象
是否属于这个类的一种(包括继承)
isMemberOfClass: 返回BOOL:一个对象
是否属于这个类的一种(不包括继承)
respondsToSelector: 返回BOOL:一个对象是否响应一个给定的方法
它可以在运行时计算这些问题的答案
给这些方法参数有点麻烦
类的测试方法用class
你通过发送类方法class到一个类来得到一个类
if ([obj isKindOfClass:[NSString class]]) {
 NSString *s = [(NSString *)obj stringByAppendingString:@”xyzzy”];
}
方法的测试方法用选择器(SEL)
特有的@selector( )指令,把一个方法名转化到一个选择器
if ([obj respondsToSelector:@selector(shoot)]) {
[obj shoot];
} else if ([obj respondsToSelector:@selector(shootAt:)]) {
[obj shootAt:target];
}
SEL是选择器的Objective-C“类型”
SEL shootSelector = @selector(shoot);
SEL shootAtSelector = @selector(shootAt:);
SEL moveToSelector = @selector(moveTo:withPenColor:);
如果你有一个SEL,你也可以要求一个对象来执行它...
在NSObject中使用performSelector:或者performSelector:withObject:方法
[obj performSelector:shootSelector];
[obj performSelector:shootAtSelector withObject:coordinate];
在NSArray中使用makeObjectsPerformSelector:方法
[array makeObjectsPerformSelector:shootSelector]; //酷吧?
[array makeObjectsPerformSelector:shootAtSelector withObject:target]; //目标是一个id
在UIButton中,- (void)addTarget:(id)anObject action:(SEL)action …;
[button addTarget:self action:@selector(digitPressed:) ...];