[精通Objective-C]对象和消息传递
参考书籍:《精通Objective-C》【美】 Keith Lee
目录
对象
创建对象
NSObject类中用于创建类实例(即对象)的方法
+(id) alloc
通过alloc方法,可以创建对象,下面以创建一个Atom对象为例
//创建一个属于Atom类的对象,并为它分配一个数据类型为id的变量
id atom = [Atom alloc];
//与上面语句等价,显示定义变量的类型,失去灵活性,但可以获得静态类型检查
Atom *atom = [Atom alloc];
初始化对象
alloc方法为对象分配了内存,并将它的实例变量设置为0,但是alloc方法既没有将该对象的实例变量初始化为适当的值,也没有为这个对象准备其他必须的对象和资源。因此自定义的类还应该实现完成初始化的过程的方法。
NSObject类中用于初始化对象的方法:
-(id) init
下面以Atom类的init方法为例:
-(id) init{
//调用父类的init方法,结果赋给调用对象
if ((self = [super init])) {
//初始化
_chemicalElement = @"None";
}
return self;
}
重构Atom类并创建子类
编译器自动补全属性时,创建的是一个作用范围为@private的支持实例变量,无法被该类的子类访问。可以重构该类,将这个变量的作用范围设置为@protected。
@interface Atom : NSObject
//重构Atom类,声明作用范围为@protected的支持实例变量
{
@protected NSUInteger _protons;
@protected NSUInteger _neutrons;
@protected NSUInteger _electrons;
@protected NSString *_chemicalElement;
@protected NSString *_atomicSymbol;
}
//之前声明的属性和方法
@property(readonly) NSUInteger protons;
@property(readonly) NSUInteger neutrons;
@property(readonly) NSUInteger electrons;
@property(readonly) NSString *chemicalElement;
@property(readonly) NSString *atomicSymbol;
-(NSUInteger) massNumber;
@end
创建一个Atom类的子类Hydrogen,并使用新的可以接受参数的初始化方法。
Hydrogen.h
#import "Atom.h"
@interface Hydrogen : Atom
//声明初始化方法
-(id) initWithNeutrons:(NSUInteger)neutrons;
@end
Hydrogen.m
#import "Hydrogen.h"
@implementation Hydrogen
{
@private HydrogenHelper *helper;
}
//实现初始化方法
-(id) initWithNeutrons:(NSUInteger)neutrons{
if ((self = [super init])) {
_chemicalElement = @"Hydrogen";
_atomicSymbol = @"H";
_protons = 1;
//使用传入参数为实例变量赋值
_neutrons = neutrons;
}
return self;
}
@end
工厂方法
工厂方法是指用于创建和初始化类的便捷方法
在Hydrogen.h中声明
+(id) hydrogenWithNeutrons:(NSUInteger)neutrons;
在Hydrogen.m实现
+(id) hydrogenWithNeutrons:(NSUInteger)neutrons{
//创建一个Hydrogen对象并初始化,然后返回这个对象
return [[self alloc] initWithNeutrons:neutrons];
}
之后我们就可以用两种不同的方法来完成对象的初始化了
//普通方法
Hydrogen *atom1 = [[Hydrogen alloc] initWithNeutrons:1];
//工厂方法
Hydrogen *atom2 = [Hydrogen hydrogenWithNeutrons:2];
消息传递
消息传递是OOP中的用于调用对象中方法的机制。接收消息的对象(接收器)会在运行时决定调用其实例的哪个方法。实例方法可以访问对象的实例变量和实例方法。
发送消息
向对象发送消息的格式:
[接收器 消息名称1:参数1 消息名称2:参数2 ...]
同时Objective-C还为类方法提供了语言级的支持:
[类名 消息名称1:参数1 消息名称2:参数2 ...]
之前的[Hydrogen hydrogenWithNeutrons:2]
就是向Hydrogen类发送消息。
消息转发
在运行时,接收器都会通过解释消息,确定需要调用哪个方法。Objective-C提供了消息转发机制,当对象收到与其方法集不匹配的消息时,通过消息转发机制可以使对象能够在收到无法识别的信息时执行各种逻辑,如将消息发送给能够作出回应的其他接收器或是既不处理也不使程序抛出运行时错误,默默吞下消息。
Objective-C为NSObject类的子类提供了两类消息转发选项。
快速转发:
//重写NSObject类的该方法,将该方法转发给其他对象,就像是将对象的实现代码与转发对象合并到了一起,类似于类实现的多重继承行为
-(id) forwardingTargetForSelector:(SEL)aSelector
标准(完整)转发:
//重写NSObject类的该方法,可以让对象使用消息中的全部内容(目标,方法名和参数)
-(void) forwardInvocation:(NSInvocation *)anInvocation
下面来为Hydrogen类添加快速转发机制。
首先需要创建一个辅助类HydrogenHelper,该类负责处理一个名为factoid的Hydrogen类无法处理的方法。
HydrogenHelper.h
#import <Foundation/Foundation.h>
@interface HydrogenHelper : NSObject
-(NSString *) factoid;
@end
HydrogenHelper.m
#import "HydrogenHelper.h"
@implementation HydrogenHelper
-(NSString *) factoid{
return @"The lightest element and most abundant chemical substance.";
}
@end
再更新Hydrogen类的代码
@implementation Hydrogen
//添加实例变量HydrogenHelper
{
@private HydrogenHelper *helper;
}
-(id) initWithNeutrons:(NSUInteger)neutrons{
if ((self = [super init])) {
_chemicalElement = @"Hydrogen";
_atomicSymbol = @"H";
_protons = 1;
_neutrons = neutrons;
//创建并初始化HydrogenHelper对象
helper = [[HydrogenHelper alloc] init];
}
return self;
}
+(id) hydrogenWithNeutrons:(NSUInteger)neutrons{
return [[self alloc] initWithNeutrons:neutrons];
}
//重写NSObject类的forwardingTargetForSelector:(SEL)aSelector方法
-(id) forwardingTargetForSelector:(SEL)aSelector{
//如果HydrogenHelper对象能够处理该消息,则将消息转发给HydrogenHelper对象
if ([helper respondsToSelector:aSelector]) {
return helper;
}
return nil;
}
@end
为了使Hydrogen类能够接收factoid方法,还需再添加一个分类,在其中声明factoid方法(不需要实现)
Atom+Helper.h(由于不需要实现方法可以把自动创建出来的Atom+Helper.m删掉)
#import "Atom.h"
//也可以将Atom改为Hydrogen
@interface Atom (Helper)
//声明factoid方法
-(NSString *) factoid;
@end
最后就可以通过消息转发功能,向Hydrogen对象发送factoid方法了
Hydrogen *atom = [Hydrogen hydrogenWithNeutrons:2];
[atom factoid];
附录:前一章节创建的Atom类
Atom.h
#import <Foundation/Foundation.h>
@interface Atom : NSObject
@property(readonly) NSUInteger protons;
@property(readonly) NSUInteger neutrons;
@property(readonly) NSUInteger electrons;
@property(readonly) NSString *chemicalElement;
-(NSUInteger) massNumber;
@end
Atom.m
#import "Atom.h"
@implementation Atom
-(id) init{
if ((self = [super init])) {
_chemicalElement = @"None";
}
return self;
}
-(NSUInteger) massNumber{
return self.protons + self.neutrons;
}
@end