首页 Objective-C 从 C++ 到 Objective-C(9):实例化

从 C++ 到 Objective-C(9):实例化

0 1.6K

类的实例化位导致两个问题:构造函数、析构函数和赋值运算符如何实现,以及如何分配内存。

在 C++ 中,变量默认是“自动的”:除非被声明为static,否则变量仅在自己的定义块中有意义。动态分配的内存可以一直使用,直到调用了free()或者delete。C++ 中,所有对象都遵循这一规则。

然而在 Objective-C 中,所有对象都是动态分配的。其实这也是符合逻辑的,因为 C++ 更加 static,而 Objective-C 则更加动态。除非能够在运行时动态分配内存,否则 Objective-C 实现不了这么多动态的特性。

构造函数和初始化函数

分配 allocation 和初始化 initialization 的区别

在 C++ 中,内存分配和对象初始化都是在构造函数中完成的。在 Objective-C 中,这是两个不同的函数。

内存分配由类方法 alloc 完成,此时将初始化所有的实例数据。实例数据将被初始化为 0,除了一个名为isaNSObject的指针。这个指针将在运行时指向对象的实际类型。实例数据根据传入的参数初始化为某一特定的值,这一过程将在一个实例方法 instance method 中完成。这个方法通常命名为init。因此,构造过程被明确地分为两步:内存分配和初始化。alloc消息被发送给类,而init消息则被发送给由alloc创建出来的新的对象。初始化过程不是可选的,alloc之后应该跟着init,之后,父类的init也会被调用,直到NSObjectinit方法。这一方法完成了很多重要的工作。

在 C++ 中,构造函数的名字是规定好的,必须与类名一致。在 Objective-C 中,初始化方法与普通方法没有什么区别。你可以用任何名字,只不过通常都是选用 init 这个名字。然而,我们还是强烈建议,初始化方法名字一定要用init或者 init 开头的字符串

使用allocinit

调用alloc之后将返回一个新的对象,并且应该给这个对象发送一个init消息。init调用之后也会返回一个对象。通常,这就是初始化完成的对象。有时候,如果使用单例模式,init可能会返回另外的对象(单例模式要求始终返回同一对象)。因此,init的返回值不应该被忽略。通常,allocinit都会在一行上。

C++

Foo* foo = new Foo;

Objective-C

Foo* foo1 = [Foo alloc];
[foo1 init]; // 这是不好的行为:应该使用 init 的返回值
Foo* foo2 = [Foo alloc];
foo2 = [foo2 init]; // 正确,不过看上去很啰嗦
Foo* foo3 = [[Foo alloc] init]; // 正确,这才是通常的做法

为检查内存分配是否成功,C++ 可以判断 new 返回的指针是否是 0(如果使用的是 new(nothrow)运算符)。在 Objective-C 中,检查返回值是否是nil就已经足够了。

初始化方法的正确示例代码

一个正确的初始化方法应该有如下特点:

  • 名字以 init 开始;
  • 返回能够使用的对象;
  • 调用父类的 init 方法,直到NSObjectinit方法被调用;
  • 保存[super init...]的返回值;
  • 处理构造期间出现的任何错误,无论是自己的还是父类的。

下面是一些代码:

C++

class Point2D
{
public:
    Point2D(int x, int y);
private:
    int x;
    int y;
};
Point2D::Point2D(int anX, int anY) {x = anX; y = anY;}
...

Point2D  p1(3,4);
Point2D* p2 = new Point2D(5, 6);

Objective-C

@interface Point2D : NSObject
{
    int x;
    int y;
}

// 注意,在 Objective-C 中,id 类似于 void *
// (id) 就是对象的“一般”类型
-(id) initWithX:(int)anX andY:(int)anY;
@end

@implementation Point2D

-(id) initWithX:(int)anX andY:(int)anY
{
    // 调用父类的初始化方法
    if (!(self = [super init])) // 如果父类是 NSObject,必须进行 init 操作
        return nil; // 如果父类 init 失败,返回 nil
    // 父类调用成功,进行自己的初始化操作
    self->x = anX;
    self->y = anY;
    return self; // 返回指向自己的指针
}
@end

...
Point2D* p1 = [[Point2D alloc] initWithX:3 andY:4];

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2