002-对象与使用

2017-11-26  本文已影响14人  Yasic

对象与使用

创建类

objective-c 中的类可以视作一种单元,也包括 .h 接口文件和 .m 定义文件两部分。对于一个类,基本元素包括数据、属性和方法等。

类的接口文件

创建一个与类名一致的 .h 接口文件,在接口文件中通过 @interface 来声明类的详细接口。

@interface Animal : NSObject
{
    NSString * name;
    NSString * size;
    NSString * color;
}
@property (nonatomic, retain) NSString * hobbit;

-(void)eat: (NSString *) food;

@end

要注意必须用 @end 结束 @interface。

在类的声明中,类名紧跟的冒号后指定类的直接父类,有且只有一个。

在紧跟类名的大括号中可以定义类的成员变量即实例变量,也称作 ivar,其作用域是作为类的一部分定义的任何方法,也即在类的方法中均可以访问,但外部无法访问。

对于类的方法,其返回类型在方法名前的括号定义,其参数在方法名后的冒号后定义。

类的定义文件

创建一个与类名一致的 .m 文件,定义文件中用 @implementation 和 @end 来定义类接口的具体实现。

@implementation Animal

@synthesize hobbit = _hobbit;

-(id)init
{
    if (self = [super init])
    {
        name = @"Animal";
    }
    return self;
}

-(void)eat: (NSString *)food
{
NSLog(@"%@ eat %@", name, food);
}

-(void)dealloc
{
    name = nil;
}

@end

方法

在 objective-c 中,调用方法又称为发送消息,objective-c 会首先寻找接收消息的对象,也叫做实例,然后通过对象的 isa 指针找到对象的类,再通过类的指针找到需要调用的方法。

1. 类的方法

类的方法是指不需要生成实际对象实例就可以调用的方法,在声明和实现时用 + 号修饰。

接口声明

+(NSString *)getType;

方法定义

+(NSString *)getType
{
    return @"Animal";
}

2. 对象方法

对象方法是只有在对象实例化以后才能调用的方法,用于计算或改变对象内的数据,用 - 号修饰。

接口声明

-(void)eat: (NSString *) food;

方法定义

-(void)eat: (NSString *)food
{
NSLog(@"%@ eat %@", name, food);
}

3. 特殊对象方法

特殊对象方法是指一些具有特定功能和标准行为的方法,最常见的有初始化方法和析构方法。

对象

对象是类的实例,声明和初始化一个对象方式如下。

Animal * animal = [[Animal alloc]init];

这里要注意,在 objective-c 中,对于对象和类的方法的调用是通过中括号来调用的,如果方法有参数则放在冒号后面

[animal eat: @"rabbit"];

alloc 函数是一个类方法,用于开辟一个内存空间给对象,它会返回一个对象,对象类型就是调用 alloc 函数的类的类型。

对于不希望被外部调用和访问的方法,可以不在接口文件中声明,但要注意,这类私有方法必须要在使用方法前声明该方法或直接定义该方法。这段描述来自书上,但我的测试来看是没有这一限制的。

属性

在 objective-c 中,属性和数据成员是不一样的,成员数据会在内存中真正存储数据,而属性只是提供对于成员数据的存取器函数,如赋值函数和取值函数。

属性声明对象的状态

在这里继续对于 Animal 类的完善,如果我们加上属性的话,那么可以写成如下形式

接口文件

@interface Animal : NSObject
{
    NSString * name;
    NSString * size;
    NSString * color;
    NSString * birthDate;
}
@property(nonatomic, retain) NSString * name;
@property(nonatomic, retain) NSString * size;
@property(nonatomic, assign) NSString * color;
@property(nonatomic, retain) NSString * birthDate;
@property(nonatomic, readonly) NSString * age;

-(id)init;
-(id)initWithName:(NSString *)name;
-(void)eat: (NSString *) food;

+(NSString *)getType;

@end

定义文件

@implementation Animal

@synthesize name;
@synthesize size;
@synthesize color;
@synthesize birthDate;
@dynamic age;

-(NSString *)calculateAge: (NSString *)birthDate
{
    return @"";
}

-(NSString *)age;
{
    return [self calculateAge:birthDate];
}

-(id)init
{
    if (self = [super init])
    {
        name = @"Animal";
    }
    return self;
}

-(id)initWithName: (NSString *) aname
{
    if (self = [self init])
    {
        self->name = aname;
    }
    return self;
}

-(void)eat: (NSString *)food
{
NSLog(@"%@ eat %@", name, food);
}

+(NSString *)getType
{
    NSLog(@"Animal");
    return @"Animal";
}

-(void)dealloc
{
    name = nil;
}

@end

这里看到对于声明的数据成员,都用同样命名的属性提供了相应的存取方法,同时还带有对于属性的描述特性,简单总结一下。

这些特性大部分与内存管理相关。

而针对定义文件,我们需要对属性进行实现,关键字有两个

这里可以看到,我们将 age 设为了 dynamic,所以我们手动提供了相应的存取器函数,在这里我们实际上没有创建一个 age 的实际数据成员,而是用计算函数来确定,这就是属性对于内部数据的隔离保护和透明存取。

没有要求属性名和数据成员名保持一致,当然如果按照习惯可以将成员数据命名前加上下划线_,在 c# 中这样的变量表示字段。

属性的使用

属性使用有两种方式,传统方式就是一般的类或对象方法的调用。

NSLog(@"%@", [animal getColor]);

但属性还支持点标记访问

NSLog(@"%@", animal.color);

这里要注意对于自定义存取器函数,传统方式只能调用定义的存取器函数来访问,因为属性本身会被内部编译成赋值方法和取值方法。

依赖关系

依赖关系是两个实体之间的关系,前面在类文件头部加入的 import 语句就是建立依赖关系。当依赖的文件发生变化时,需要重新编译所依赖的文件,这种重新编译是从缘端至最末端的,因此有时会带来严重的编译问题。

所以可以用 @class 关键字代替 import语句,告知编译器当前依赖的文件仅仅是一个指向对象的指针,依据objective-c 的动态内存特性,可以稍后再得知其具体的内存空间。同时对于相互依赖的文件,用 import 语句也会报错,可以用 @class 代替。

但对于继承关系来说,由于编译器需要知道超类的所有信息才可以成功编译子类,所以依赖关系不能用 @class 代替。

上一篇下一篇

猜你喜欢

热点阅读