Objective-C 苹果开发文档 04 Encapsulating Data

来源:互联网 发布:软件改变生活 编辑:程序博客网 时间:2024/05/20 18:45

Encapsulating Data

除了前一章所说的消息传递之外,一个对象也可以通过她的属性对数据进行封装。

这章会讲到OC语法中声明对象的属性,解释了这些属性是如何通过访问器方法和实例变量被默认实现的。如果你一个属性是由实例变量构成,那么变量必须使用正确的初始化方法。

如果一个对象需要与另一个对象保持联系,那么,你应该考虑好两个对象之间的关系性质。尽管OC中对象的内存管理大多数情况下是通过ARC进行的,但是,知道如何避免像强指针引用周期这样的问题是很重要的,因为它会导致内存的泄露问题。本章会向您解释一个对象的生命周期问题,以及明确的思考如何管理图形对象。

Properties Encapsulate an Object’s Values


大多数的对象都需要跟踪信息以便执行她们的任务。一些对象的模型被设计成一个或多个值,比如Cocoa框架中的NSNumber类,既可以存储一个数据值,也可以存储一个自定义的XYZPerson类,这个类的模型有一个姓和名属性。一些对象则是处理更普遍的数据在她的范围内即使是这些对象需要跟踪用户界面元素或相关的模型对象,比如处理一个用户界面和显示信息的交互问题。

Declare Public Properties for Exposed Data


objective - c属性提供了一种定义是为了封装类的信息,就像你在 Properties Control Access to an Object’s Values中看到的那样:

@interface XYZPerson : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

在这个例子中,XYZPerson类声明了字符串属性用来存储姓名。

基于面向对象编程中一个对象应该在公共接口隐藏其内部的实现这个基本的原则,一个对象通过方法访问她的属性要比直接访问属性要好。

Use Accessor Methods to Get or Set Property Values

你在存取一个对象的属性值时需要通过访问器方法实现:

    NSString *firstName = [somePerson firstName];
    [somePerson setFirstName:@"Johnny"];

默认情况下,这些访问器方法是由编译器自动生产的,所以你只需要在类接口中使用@property声明属性就可以了。

合成方法遵循特定的命名约定:

  • 该方法用于访问值(getter方法),值的名称必须和属性中定义的名称相同。

    属性的getter方法调用名和属性的名称同为firstName 。

  • setter方法名以“set”开始,然后使用大写的属性名。

    setter方法调用名为setFirstName,属性名为firstName 。

如果你不想让属性的值通过setter方法被更改,你可以添加一个参数像这样:

@property (readonly) NSString *fullName;

上面的代码除了显示其他对象是如何支持与其属性交互的,参数也告诉编译器怎样合成相关的访问器方法。

在这个例子中,编译器会合成一个fullname 获取方法,但是不会合成setFullName 设置方法。

Note: 与参数readonly相反的是readwrite,但是你不需要明确的使用readwrite 参数,因为这个参数是默认的。


如果你想使用一个不同的名字给一个访问器方法,可以通过添加属性来指定一个自定义名称。在Boolean这个属性的例子中(属性有两个值YES或者NO),get方法通常使用的名称是以“is”开始的。例如get方法叫finished,那么应该叫isFinished。

同样的,在property后添加属性:

@property (getter=isFinished) BOOL finished;

If you need to specify multiple attributes, simply include them as a comma-separated list, like this:

@property (readonly, getter=isFinished) BOOL finished;

In this case, the compiler will synthesize only an isFinished method, but not a setFinished: method.

Note: 一般来说,属性的访问器方法应该与键值编码兼容,这就意味着她们需要遵循一些命名的约定。详见Key-Value Coding Programming Guide

Dot Syntax Is a Concise Alternative to Accessor Method Calls

不但使用明确的访问器方法调用,而且OC还提供了一个替代品点语法用来访问对象的属性。

点语法允许你像这样访问属性:

    NSString *firstName = somePerson.firstName;
    somePerson.firstName = @"Johnny";


点语法纯粹是一个方便的并且包装了的访问器方法。当你使用点语法,属性仍然是使用上面提到的getter和setter方法被访问或被更改:

  • Getting a value using somePerson.firstName is the same as using [somePerson firstName]

  • Setting a value using somePerson.firstName = @"Johnny" is the same as using [somePerson setFirstName:@"Johnny"]

这就意味着属性同样是受到参数的约束的。如果一个属性被标记为readonly,那么你使用点语法设置器就会得到一个编译器的错误。

Most Properties Are Backed by Instance Variables

默认情况下,一个实例变量的属性是可读写的readwrite,编译器会自动合成她的访问器方法。 

一个实例变量就是一个变量,这个变量有她的值在对象的生命周期内。内存会通过alloc方法的调用为实例变量分配内存,当对象被释放的时候实例变量也被释放。

除非特别的指定,否则实例变量的声明会自动用下划线做前缀。例如,一个属性的方法叫firstName,自动生成的实例变量会叫_firstName。

尽管习惯上一个对象会有她自己的属性访问器方法或者使用点语法访问属性,但是你也可以直接使用实例任何在类中声明的实例方法存取实例变量。

- (void)someMethod {
    NSString *myString = @"An interesting string";
 
    _someString = myString;
}

这个例子中,myString是个局部变量,_someString  是一个实例变量。

一般情况下,你应该使用访问器方法或者点语法来存取属性值,即使这个对象的属性是来自她自己的实现。可以使用self啊:

- (void)someMethod {
    NSString *myString = @"An interesting string";
 
    self.someString = myString;
  // or
    [self setSomeString:myString];
}

这条规则的例外是在编写初始化方法时,回收或自定义访问器方法,本节后面会有介绍。

You Can Customize Synthesized Instance Variable Names


正如前面提到的,一个可写属性的默认行为是使用一个名为_propertyName的实例变量。

如果你想使用一个不同的实例变量的名称,您需要直接告诉编译器使用以下语法实现:

@implementation YourClass
@synthesize propertyName = instanceVariableName;
...
@end

For example:

@synthesize firstName = ivar_firstName;

在这种情况下,属性调用仍然叫做firstName,而且仍然可以通过firstName和setFirstName: 的访问器方法或者点语法调用,只不过返回的实例变量被叫做ivar_firstName。

Important: 如果你使用@synthesize但是没有指定实例变量的名字,像这样:

@synthesize firstName;
这个实例变量会承担同样的名字作为属性的名字。

在这个例子中,实例变量名是没有下划线的firstName。

You Can Define Instance Variables without Properties


在任何时候,如果你需要追踪一个值或者对象,为对象添加property是很好的惯例。

如果你确实需要通过不声明property定义你自己的实例变量,那么你可以把她们添加在在接口或实现的扩后里,像这样:

@interface SomeClass : NSObject {
    NSString *_myNonPropertyInstanceVariable;
}
...
@end
 
@implementation SomeClass {
    NSString *_anotherCustomInstanceVariable;
}
...
@end

Note: 你还可以将实例变量添加在类的扩展的顶部,详见 Class Extensions Extend the Internal Implementation.

Access Instance Variables Directly from Initializer Methods


Setter方法可能会有额外的副作用。她们或许会触发KVC键值编码通知,或者进一步执行你自定义的任务。

你应该通过一个初始化方法访问直接访问实例变量,因为当你在定义属性property的时候,对象的其他部分可能还没有被完全初始化。即使你没有提供自定义访问器方法或者你知道你自己类的副作用,但是以后的子类可能会覆盖这个方法。

A typical init method looks like this:一个典型的init方法

- (id)init {
    self = [super init];
 
    if (self) {
        // initialize instance variables here
    }
 
    return self;
}

一个init方法在做她自己的初始化工作之前应该分配self接收调用父类的初始化方法。父类可能在初始化一个对象的时候失败或者返回nil,所以在执行你自己的初始化方法之前,你应该检查一下确保self不是nil值。

通过调用[super init],一个对象会有序的通过根类到每个子类的初始化方法实现被初始化。如图:


Figure 3-1  The initialization process


正如你在前面所看到的那样,一个对象或者调用init方法初始化,或者通过调用一个有指定值的方法来初始化。

在XYZPerson类中,提供一个可以初始化人姓名的方法:

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName;

You’d implement the method like this:

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName {
    self = [super init];
 
    if (self) {
        _firstName = aFirstName;
        _lastName = aLastName;
    }
 
    return self;
}

The Designated Initializer is the Primary Initialization Method

如果一个对象声明一个或者多个初始化方法,你应该决定哪个方法是指定的初始化方法(指定初始化器)。这个方法通常是那个提供最多选择的(比如方法有最多的参数),其他方法调用的时候会很方便。你应该重写一个典型的init方法,这个方法可以指定一个合适的默认值。

如果XYZPerson类也有一个生日的属性,那么她的指定的初始化器会是这样:

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName
                                            dateOfBirth:(NSDate *)aDOB;

如上面所示,该方法会设置相关的实例变量。如果你任然希望为姓名提供一个方便的初始化器,你可以调用实现指定的初始化器,像这样:

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName {
    return [self initWithFirstName:aFirstName lastName:aLastName dateOfBirth:nil];
}

You might also implement a standard init method to provide suitable defaults:

- (id)init {
    return [self initWithFirstName:@"John" lastName:@"Doe" dateOfBirth:nil];
}

当子类有许多init方法的时候,如果你需要写一个初始化方法,你可以覆盖父类的指定初始化器,或者添加你自己额外的初始化方法。不管怎样,你都应该在实现自己的方法之前调用父类的初始化器(替代[super init]的值)。

You Can Implement Custom Accessor Methods

Properties don’t always have to be backed by their own instance variables.

属性并不总是需要被他们自己的实例变量支持。

例子XYZPerson类中可以为人的全名定义一个只读的属性:

@property (readonly) NSString *fullName;

与其每次名字更新的时候必须要更新全名的属性,不如使用更简单的方法,自定义一个访问器方法来创建全名的需求:

- (NSString *)fullName {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}

这个简单的例子使用了格式化字符串和说明符(前面的章节描述过)来创建一个字符串,这个字符串包含一个人的姓名并且用空格分开。

Note: 尽管这是一个方便的例子,但是她是区域设置的,因为她只适用于那些人名在姓之前的国家。

如果你需要写一个自定义的访问器方法为一个只使用一个实例变量的属性,你必须允许实例变量直接访问这个方法。例如,延迟一个属性的初始化器直到她的第一次调用请求,像是使用一个“lazy accessor”,这样:

- (XYZObject *)someImportantObject {
    if (!_someImportantObject) {
        _someImportantObject = [[XYZObject alloc] init];
    }
 
    return _someImportantObject;
}

在返回值之前,这个方法第一次检查_someImportantObject 实例变量的值是否是nil,如果是,她会分配一个对象。

Note: 编译器会在所有情况下自动合成一个实例变量,她也会自动合成至少一个访问方法。如果你为一个readwrite属性实现了getter和setter方法,或者为readonly属性实现了getter方法,编译器会假定你已经接管了属性的实现,然后她就不会自动合成实例变量了。

如果你仍然需要一个实例变量,你可以通过如下方法:

@synthesize property = _property;

Properties Are Atomic by Default


默认情况下,一个OC的属性是atomic(原子性):

@interface XYZObject : NSObject
@property NSObject *implicitAtomicObject;          // atomic by default
@property (atomic) NSObject *explicitAtomicObject; // explicitly marked atomic
@end

这就意味着合成的访问器确保一个值总是完全的被getter方法访问或者完全被setter方法设置,即使访问器被不同的线程同时调用。

因为访问器方法内部的实现和atomic的同步是私有的,所以不可能让你自己实现的访问器方法和一个合成的访问器方法联合在一起。如果你尝试这样做,你会得到一个编译器警告,因为你提供了一个自定义的setter给一个atomi,readwrite属性但是却没有让编译器合成getter方法。

你可以使用nonatomic属性参数指定一个合成的访问器方法直接返回或者设置一个值,但是这样做的后果是你不会保证当同一个值被不同的线程同时访问会发生什么情况。由于这个原因,访问一个nonatomic属性会比访问一个atomic属性要快,例如,你可以把自己定义的getter方法和自动合成的setter方法联合到一起。

@interface XYZObject : NSObject
@property (nonatomic) NSObject *nonatomicObject;
@end
@implementation XYZObject
- (NSObject *)nonatomicObject {
    return _nonatomicObject;
}
// setter will be synthesized automatically
@end

Note: 属性的原子性并不等同于一个对象的线程安全。

考虑这样的一个XYZPerson对象,两个人的姓名被同一个线程的atomic访问器修改。如果另一个线程在同一时间访问这两个姓名,原子性的getter方法会返回完整的字符串(没有崩溃),但是不保证这些值对于两个人是正确的。如果第一个名字在被更改之前访问,但是另一个是在更改之后,那么你得到的就是两个不匹配的姓名。

这个例子很简单,但是当你考虑到跨网络的对象之间的问题时,线程的安全问题会变得更复杂,详见Concurrency Programming Guide。


Manage the Object Graph through Ownership and Responsibility

正如你所看到的那样,OC中对象的内存是动态分配的(在堆区),也就意味着你需要使用指针追踪一个对象的地址。与标量值不同的是,你不可能总是通过一个指针变量的范围来确定一个对象的生命周期。相反,一个对象必须总是在内存中保持活跃状态,知道其他的对象不需要它。

与其试图担心经常管理每个对象的生命周期,不如去考虑一下对象之间的关系。

例如,在XYZPerson对象中,firstName和lastName这两个字符串属性实际上是被XYZPerson实例“owned”的。这就意味着他们应该待在内存中知道XYZPerson对象不在内存中为止。

当一个对象以这种方式依赖于其他对象,实际上是获得了其他对象的所有权,第一个对象被说成是其他对象的强引用。在OC中,一个对象只要还有一个强引用就不会被释放内存空间。XYZPerson实例和她的NSString对象如下所示:


Figure 3-2  Strong Relationships

当一个XYZPerson对象被分配内存空间的时候,这两个字符串对象也会分配内存空间,假设没有其他的强引用。

添加一点复杂性给这个例子,考虑一个应用程序的对象图如图3 - 3所示。

Figure 3-3  The Name Badge Maker application

当用户点击更新按钮时,标记的预览视图会更新和名字相关的信息。

一个人的细节信息第一次被载入并且更新按钮被点击,简化的对象视图如下所示:

Figure 3-4  Simplified object graph for initial XYZPerson creation

When the user modifies the person’s first name, the object graph changes to look like Figure 3-5.

Figure 3-5  Simplified object graph while changing the person’s first name

标记显示图包含一个强引用对于原始的@“John”字符串对象,即使XYZPerson对象现在又一个不同的firstName。这就意味着@“John”对象还会待在内存中,被标记视图用来打印名字。

一旦用户第二次点击更新按钮,标记视图会被告之需要更新自己的内部属性用来与人这个对象匹配,所以对象视图如下所示这样:

Figure 3-6  Simplified object graph after updating the badge view

在这一点上,原始的@“John”对象再也没有强引用指向它了,所以她会在内存中被清除。

默认情况下,OC的属性和变量都包含强引用对他们的对象。大多数情况下没问题,但是会有一个潜在的问题在强引用循环的时候。

Avoid Strong Reference Cycles


尽管强引用在单向的对象关系之间表现的不错,但是你需要在一组关联对象的处理中加倍小心。如果一组对象被一圈强关系关联,即使已经没有强引用来自外部的对象了,他们之间仍然会保持彼此的活跃。

一个明显的例子就是在一个桌面视图(UITableView是iOS系统的,NSTableView是OS X系统的)和她的代理之间存在一个潜在的引用循环。为了使一个普通的桌面视图类在大多数情况下都有用,她的代理会处理一些决定对于外部对象。这就意味着她依赖于其他对象决定什么内容应该显示,或者在这个桌面视图中如果用户与一个特殊的入口交互应该做什么。

普遍的方案是视图与代理之间互相引用,如图所示:

Figure 3-7  Strong references between a table view and its delegate

A problem occurs if the other objects give up their strong relationships to the table view and delegate, as shown in Figure 3-8.

Figure 3-8  A strong reference cycle


Even though there is no need for the objects to be kept in memory—there are no strong relationships to the table view or delegate other than the relationships between the two objects—the two remaining strong relationships keep the two objects alive. This is known as a strong reference cycle.

即使不需要对象保留在内存中--除了两个对象之间的关系外,没有强关系在视图或者代理之间--两个剩下的强关系任然会保留两个对象在内存中。这个被叫做强引用循环。

解决这个问题的方法就是让其中的一个强引用变成弱引用。一个弱引用并不意味着所有权或者责任问题,也不意味着不让一个对象保持在内存中。

如果桌面视图使用一个弱引用对她的代理(这就是UITableView和NSTableView如何解决的这个问题),最初的对象视图现在是这样的:

Figure 3-9  The correct relationship between a table view and its delegate


When the other objects in the graph give up their strong relationships to the table view and delegate this time, there are no strong references left to the delegate object, as shown in Figure 3-10.

Figure 3-10  Avoiding a strong reference cycle


This means that the delegate object will be deallocated, thereby releasing the strong reference on the table view, as shown in Figure 3-11.

Figure 3-11  Deallocating the delegate


Once the delegate is deallocated, there are no longer any strong references to the table view, so it too is deallocated.

Use Strong and Weak Declarations to Manage Ownership

By default, object properties declared like this:

@property id delegate;

use strong references for their synthesized instance variables. To declare a weak reference, add an attribute to the property, like this:

@property (weak) id delegate;

Note: The opposite to weak is strong. There’s no need to specify the strong attribute explicitly, because it is the default.强引用是默认的。

局部变量(非属性实例变量non-property)默认情况下也是强引用对于对象,这就意味着下面的代码会是你希望的那样运行:

    NSDate *originalDate = self.lastModificationDate;
    self.lastModificationDate = [NSDate date];
    NSLog(@"Last modification date changed from %@ to %@",
                        originalDate, self.lastModificationDate);

本例中,局部变量originalDate对于初始的lastModificationDate对象保持强引用。当lastModificationDate属性改变时,属性再也不会对原来的date保持强引用,但是date仍然会存在于originalDate的强引用变量中。

Note: 一个变量会保持强引用对于一个对象,只要变量还在生命周期范围内,或者直到变量被重新分配给另一个对象或者被赋值为nil。

If you don’t want a variable to maintain a strong reference, you can declare it as __weak, like this:

    NSObject * __weak weakVariable;

弱引用并不需要保持一个对象一直活着,可能在引用还被使用的时候引用的对象已经被回收了。为了避免现在被回收的对象指向已经被占用的内存空间引发的悬空指针问题,当她的对象被回收时,弱引用会被自动设置为nil。

这意味着如果你使用弱指针变量在前面date例子中:

    NSDate * __weak originalDate = self.lastModificationDate;
    self.lastModificationDate = [NSDate date];

originalDate变量可能潜在的被设置为nil。当self.lastModificationDate被重新分配值时,属性再也不会保持强引用对原始的date。如果再没有其他的强引用对她,那么原始值会被回收,originalDate被设置为nil值。

弱指针引用可能会造成一种困惑,尤其是在这样的代码中:

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

In this example, the newly allocated object has no strong references to it, so it is immediately deallocated and someObject is set to nil.

Note: The opposite to __weak is __strong. Again, you don’t need to specify __strong explicitly, because it is the default.

It’s also important to consider the implications of a method that needs to access a weak property several times, like this:

考虑一个需要多次访问一个弱引用属性的方法含义也很重要,像这样:

- (void)someMethod {
    [self.weakProperty doSomething];
    ...
    [self.weakProperty doSomethingElse];
}

In situations like this, you might want to cache the weak property in a strong variable to ensure that it is kept in memory as long as you need to use it:

在这种情况下,你可能想用一个强引用变量缓存弱引用属性以确保只要你需要她,她就在内存中存在:

- (void)someMethod {
    NSObject *cachedObject = self.weakProperty;
    [cachedObject doSomething];
    ...
    [cachedObject doSomethingElse];
}

在这个例子中,cacheObject变量对原来的弱引用属性保持强引用,因此只要cacheObject生命周期还在范围内(没有被分配其他的变量),她就不会被回收。

这件事特别的重要,你应该记住你需要确保一个 弱引用在使用之前不是nil。只是检查一下是不够的,像这样:

    if (self.someWeakProperty) {
        [someObject doSomethingImportantWith:self.someWeakProperty];
    }


因为在一个多线程的应用程序中,属性可能在测试与方法调用之间被回收,就会呈现出这种测试无用的情况。相反,你需要声明一个强引用的局部变量来缓存这个值,像这样:

    NSObject *cachedObject = self.someWeakProperty;           // 1
    if (cachedObject) {                                       // 2
        [someObject doSomethingImportantWith:cachedObject];   // 3
    }                                                         // 4
    cachedObject = nil;                                       // 5

本例中,强引用在第一行创建,意味着对象被保证在检查和调用的时候是没有被回收的。在第五行,cacheObject设置成nil,从而放弃强引用。如果原始的对象没有强引用指向她,她就会被回收,someWeakProperty会被设置为nil。

Use Unsafe Unretained References for Some Classes


在Cocoa和Cocoa Touch框架中还有一些类不支持弱引用,这就意味着你不能声明一个弱引用属性或者弱引用的变量用来追踪他们。这些类包括NSTextView,NSFont,NSColorSpace类;全部的列表参加:Transitioning to ARC Release Notes。

如果你需要为这些类中的一个使用弱引用,你必须使用一个不安全的引用。对属性来说,这意味着使用unsafe_unretained参数:

@property (unsafe_unretained) NSObject *unsafeProperty;


For variables, you need to use __unsafe_unretained:

    NSObject * __unsafe_unretained unsafeReference;

不安全引用与一个弱引用类似,她并不保持相关的对象不被回收,但是如果目标对象被回收,她不能被设置为nil。这意味着你会留下一个悬空指针,指向的内存是已经被回收的对象分配的内存空间,因此术语“unsafe.”发送消息给一个悬空指针会导致程序崩溃。

Copy Properties Maintain Their Own Copies


在一些情形中 ,一个对象可能希望保持她自己的一些对象的副本,用来设置她的属性。

例如,类接口XYZBadgeView像这样:

@interface XYZBadgeView : NSView
@property NSString *firstName;
@property NSString *lastName;
@end

Two NSString properties are declared, which both maintain implicit strong references to their objects.

Consider what happens if another object creates a string to set as one of the badge view’s properties, like this:

    NSMutableString *nameString = [NSMutableString stringWithString:@"John"];
    self.badgeView.firstName = nameString;

这样做非常有效,因为NSMutableString是NSString的子类。尽管标记视图认为在处理一个NSString实例,但是实际上是在处理一个NSMutableString。

This means that the string can change:

    [nameString appendString:@"ny"];

本例中,尽管在原始的标记视图firstName属性设置中名字是John,但是因为可变字符串的改变现在的名字是Johnny。

你可能会选择让标记视图保持她自己的姓名属性字符串的副本,以便有效的捕获字符串在属性被设置的时候。你可以在两个属性声明的时候添加copy参数:

@interface XYZBadgeView : NSView
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;
@end

the view now maintains its own copies of the two strings. Even if a mutable string is set and subsequently changed, the badge view captures whatever value it has at the time it is set. For example:

视图现在拥有两个字符串的副本。虽然是设置了一个可变的字符串,随后也改变了原本的值,这个标记视图捕获了无论什么值在她设置的时候。例如:

    NSMutableString *nameString = [NSMutableString stringWithString:@"John"];
    self.badgeView.firstName = nameString;
    [nameString appendString:@"ny"];

这次,标记视图拥有的firstnName将会是一个不受影响的原始John字符串的副本。

copy参数意味着属性会使用一个强引用,因为她必须紧紧抓住她创建的新对象。


Note: Any object that you wish to set for a copy property must support NSCopying, which means that it should conform to the NSCopying protocol.

Protocols are described in Protocols Define Messaging Contracts. For more information on NSCopying, see NSCopying or the Advanced Memory Management Programming Guide.

如果你需要直接设置一个copy属性的实例变量,例如在一个初始化方法中,别忘了设置一个原始对象的副本:

- (id)initWithSomeOriginalString:(NSString *)aString {
    self = [super init];
    if (self) {
        _instanceVariableForCopyProperty = [aString copy];
    }
    return self;
}

Exercises练习

  1. Modify the sayHello method from the XYZPerson class to log a greeting using the person’s first name and last name.

  2. Declare and implement a new designated initializer used to create an XYZPerson using a specified first name, last name and date of birth, along with a suitable class factory method.

    Don’t forget to override init to call the designated initializer.

  3. Test what happens if you set a mutable string as the person’s first name, then mutate that string before calling your modified sayHello method. Change the NSString property declarations by adding the copy attribute and test again.

  4. Try creating XYZPerson objects using a variety of strong and weak variables in the main() function. Verify that the strong variables keep the XYZPerson objects alive at least as long as you expect.

    In order to help verify when an XYZPerson object is deallocated, you might want to tie into the object lifecycle by providing a dealloc method in the XYZPerson implementation. This method is called automatically when an Objective-C object is deallocated from memory, and is normally used to release any memory you allocated manually, such as through the Cmalloc() function, as described in Advanced Memory Management Programming Guide.

    For the purposes of this exercise, override the dealloc method in XYZPerson to log a message, like this:

    - (void)dealloc {
        NSLog(@"XYZPerson is being deallocated");
    }

    Try setting each XYZPerson pointer variable to nil to verify that the objects are deallocated when you expect them to be.

    Note: The Xcode project template for a Command Line Tool use an @autoreleasepool { } block inside the main() function. In order to use the Automatic Retain Count feature of the compiler to handle memory management for you, it’s important that any code you write in main() goes inside this autorelease pool block.

    Autorelease pools are outside the scope of this document, but are covered in detail in Advanced Memory Management Programming Guide.

    When you’re writing a Cocoa or Cocoa Touch application rather than a command line tool, you won’t usually need to worry about creating your own autorelease pools, because you’re tying into a framework of objects that will ensure one is already in place.

  5. Modify the XYZPerson class description so that you can keep track of a spouse or partner.

    You’ll need to decide how best to model the relationship, thinking carefully about object graph management.

0 0
原创粉丝点击