Objective-C 基础学习之使用对象

By | 2014/04/14

Working with objects

基于objective-c的应用程序就是一个不同对象相互发送接收消息的这么一个生态环境。这些对象可以使cocoa或cocoa touch中的,也可以是你自己定义的。上一篇博客大体介绍了一下定义一个类的基本语法,本篇博客将介绍如何向一个对象发送一个消息也会涉及一些objective-c的动态特性。

对象发送和接收消息

在Objective-C中与多种发送消息的方式到目前为止最普通最常用的方法是使用中括号,如下所示:

[someObject dosomething]

上述代码左边左边的引用,someObject在这种情况下是一个消息的接收者。右边是消息,dosomething是调用receiver的方法的名字。换句话说,上述代码被执行的时候,someObject将会发送dosomething消息。

我们在这里声明并实现一个类:

@interface XYZPerson : NSObject
- (void)sayHello;
@end
@implementation XYZPerson<
- (void)sayHello {
NSLog(@"Hello, world!");}
@end

假设已经定义了一个XYPerson对象somePerson,我们可以用[somePerson sayHello];方法来发送消息。发送一条消息在概念上非常想调用C语言中的函数。下图说明了消息发送的过程:

使用指针来引用对象

在OC中object的存储位置一律是堆,所以必须使用一个指针指向这个对象后再通过指针来掉用对象。而OC中通过int double long之类的关键字定义出来的变量通常存在栈上。存在栈上的变量当方法执行完后就会被释放,而在堆上的对象则不会释放,他需要根据程序员给定的规则来释放。

Objects Can Send Messages to Themselves

<br />@implementation XYZPerson<br />- (void)sayHello {<br />    [self saySomething:@"Hello, world!"];<br />}<br />- (void)saySomething:(NSString *)greeting {<br />    NSLog(@"%@", greeting);<br />}<br />@end<br />

原理图:

  对象可以调用其父类实现的方法

在OC中另外一个关键字是super。通过super可以调用父类中实现好了的方法,super在重写父类方法的时候经常用到。下面实例说明了方法的重写。

<br />@interface XYZShoutingPerson : XYZPerson<br />@end<br />@implementation XYZShoutingPerson<br />- (void)saySomething:(NSString *)greeting {<br />    NSString *uppercaseGreeting = [greeting uppercaseString];<br />    NSLog(@"%@", uppercaseGreeting);<br />}<br />@end<br />

在本例中传来的string的字母全部被改为大写后在打印,这主要是与父类的saysomething作区分。当调用[someShoutingPerson sayHello];后被执行的代码是被重写过的。原理图如下:

当我们以如下的方式重写方法时:

@interface XYZShoutingPerson : XYZPerson
@end
@implementation XYZShoutingPerson
- (void)saySomething:(NSString *)greeting {
   NSString *uppercaseGreeting = [greeting uppercaseString];
   NSLog(@"%@", uppercaseGreeting);
}
@end

其调用原理如下:

对象的动态创建

在Objective0-C中内存是动态分配的,在创建一个对象时第一步是先确保内存有足够的空间能够分配给这个对象,在开辟内存时不仅要给该对象的属性开辟内存而且还要为其父类中的属性开辟空间。

在NSObject这个根类中已经写好了alloc当我们需要实例化某一个类时直接调用该方法即可。

+(id)alloc;

注意这个方法的返回值是一个id,这是OC中的一个特殊的一个关键字意思是某一种对象。id是一个指向对象的指针就像(NSObject *),关于id我会在后面的博客中详细的介绍。

在调用alloc方法时应该结合init方法

– (id)init;

init方法主要是确保每一个属性值都能够被初始化,在后文中我也会详细解释init的作用。

注意init方法的返回值也是id。

在下例中我们将newObject变量的指针指向新创建的对象实例。

在最内部的最先被调用所以NSObject发送了一个alloc消息,之后返回一个分配好内存的NSObject实例的引用。接着发送init message紧接着将初始化好的对象的指针返回给newObject变量,如下如所示:

注意:alloc和初始化不要分开,就像下面这种情况:

NSObject *someObject = [NSObject alloc];
[someObject init];

不要分开的原因是init返回的对象可能跟alloc返回的对象不一样。上例中alloc返回的对象就没有被初始化。

创建一个对象还可以使用工厂方法:

+ (NSNumber *)numberWithBool:(BOOL)value;
+ (NSNumber *)numberWithFloat:(float)value;
+ (NSNumber *)numberWithInt:(int)value;
+ (NSNumber *)numberWithLong:(long)value;

NSNumber *magicNumber = [NSNumber numberWithInt:42];

可以使用new来创建一个对象,但是其初始化方法不需要参数。

XYZObject *object = [XYZObject new];
// is effectively the same as:
XYZObject *object = [[XYZObject alloc] init];

下面的代码中提供了一种简便的创建对象的方法:

NSNumber *myBOOL = @YES;
NSNumber *myFloat = @3.14f;
NSNumber *myInt = @42;
NSNumber *myLong = @42L;

在后文中将阐述如何快速创建NSArray以及NSDictionary对象。

Objective-C是一种动态语言

上文提到过,你需要一个指针来跟踪内存中的一个对象,那么这是因为Objective-C语言的动态特性,你的指针使用什么特定的类型都不重要,只要你给他发送消息那么与该对象相关的方法就会被调用。

id类型是一种通用的对象指针,我们也可以用id来声明一个变量,但是这样你将会失去编译时有关该对象的信息,直观体现是不会在IDE中获得提示功能。考虑一下下面的代码:

id someObject = @"Hello, World!";
[someObject removeAllObjects];

在这种情况下someObject将会指向一个NSString的实例,但是编译器除了知道这是某种类型的对象外别的他一无所知。removeAllObject消息是由Cocoa或Cocoa Touch定义的,例如由NSMutableArray定义,所以编译器并不报错,但是这段代码可能会在运行时产生一个异常,因为NSString类中并没有能够响应removeAllObject消息的代码。

重写这段代码以使用oc的静态形式:

NSString *someObject = @"Hello, World!";
[someObject removeAllObjects];

这个时候编译器将会产生一个错误因为removeAllObjects没有在NSString中声明该方法。

因为一个类的对象是在运行时决定的,所以当你在创建一个实例时将其引用赋值给什么类型的变量都无所谓。 如下面这段代码:

XYZPerson *firstPerson = [[XYZPerson alloc] init];
XYZPerson *secondPerson = [[XYZShoutingPerson alloc] init];
[firstPerson sayHello];
[secondPerson sayHello];

尽管firstPrson和secondPerson在编译阶段类型都是XYZPerson的对象,但是secondPerson将会在运行时指向XYZShoutingPerson对象。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.