- 15.4 同步AddressCard方法
- import<Foundation/NSObject.h>
- import<Foundation/NSString.h>
- import“AddressCard.h”
- import<Foundation/Foundation.h>
- import“AddressCard.h”
- import<Foundation/NSArray.h>
- import“AddressCard.h”
- import“AddressBook.h”
- import“AddressBook.h”
- import<Foundation/NSAutoreleasePool.h>
- import“AddressBook.h”
- import<Foundation/NSAutoreleasePool.h>
- import<Foundation/NSArray.h>
- import“AddressCard.h”
- import“AddressBook.h”
- import<Foundation/NSAutoreleasePool.h>
15.4 同步AddressCard方法
我们已经讨论了编写访问器方法setName:和setEmail:,你也已经理解了那些重要原理,那么可以返回操作并使系统生成访问器方法。考虑AddressCard界面文件的第二个版本:
import<Foundation/NSObject.h>
import<Foundation/NSString.h>
@interface AddressCard:NSObject
{
NSString*name;
NSString*email;
}
@property(copy, nonatomic)NSStringname,email;
-(void)print;
@end
程序行
@property(copy, nonatomic)NSStringname,email;
列出属性的copy属性和nonatomic属性。如你所编写的版本一样,copy属性将在setter方法内生成实例变量的副本。默认行为不会生成副本,而是仅仅执行分配(为默认assign属性),这是我们当前讨论的一个不正确方法。
nonatomic属性指明在返回值之前,getter方法不会保留或自动释放实例变量。本书第18章将详细介绍这个议题。
代码清单15-9是新的AddressCard实现文件,该文件指明访问器方法将被同步。
代码清单15-9带同步方法的实现文件AddressCard.m
import“AddressCard.h”
@implementation AddressCard
@synthesize name, email;
-(void)print
{
NSLog(@“===================================”);
NSLog(@);
NSLog(@“%-31s|”,[name UTF8String]);
NSLog(@“%-31s|”,[email UTF8String]);
NSLog(@);
NSLog(@);
NSLog(@);
NSLog(@“O O|”);
NSLog(@“===================================”);
}
@end
这里为你准备了一个练习,即验证新的AddressCard定义与其同步访问器方法处理代码清单15-9中测试程序的情况。
现在,为AddressCard类添加另一个方法。你可能想使用一个调用同时设置卡片的姓名和email这两个字段,为此,可添加一个新方法:setName:andEmail:。[1]下面就是这个新地方法:
-(void)setName:(NSString)theName andEmail:(NSString)theEmail
{
self.name=theName;
self.email=theEmail;
}
通过使用同步setter方法设置适当的实例变量(而不是直接在方法中设置它们),会增加一定的抽象性,因此使程序更进一步地独立于它的内部数据结构。你也可以利用同步方法的属性,在本例中,是为实例变量复制而不是分配值。
代码清单15-9测试了新方法。
代码清单15-9测试程序
import<Foundation/Foundation.h>
import“AddressCard.h”
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
NSString*aName=@“Julia Kochan”;
NSString*aEmail=@“jewls337@axlc.com”;
NSString*bName=@“Tony Iannino”;
NSString*bEmail=@“tony.iannino@techfitness.com”;
AddressCard*card1=[[AddressCard alloc]init];
AddressCard*card2=[[AddressCard alloc]init];
[card1 setName:aName andEmail:aEmail];
[card2 setName:bName andEmail:bEmail];
[card1 print];
[card2 print];
[card1 release];
[card2 release];
[pool drain];
return 0;
}
代码清单15-9输出
====================================
| |
|Julia Kochan|
|jewls337@axlc.com|
| |
| |
| |
|O O|
====================================
====================================
| |
|Tony Iannino|
|tony.iannino@techfitness.com|
| |
| |
| |
|O O|
====================================
你的AddressCard类看起来工作良好,但如果要使用很多AddressCard,该怎么办呢?把它们集中到一起很合理,通过定义一个名为Address Book的新类就可以实现这项任务。AddressBook类存储地址簿的名字和一个AddressCard集合,你将这个集合存储在一个数组对象中。首先,需要以下功能:创建新的地址簿,向其添加地址卡片,计算地址簿的记录数,列出地址簿的内容;随后,可能需要更多的功能,如:搜索地址簿,删除记录,可能编辑现有记录,将记录排序,甚至拷贝记录内容。
首先看一个简单的interface文件(参见代码清单15-10)。
代码清单15-10 Addressbook.h接口文件
import<Foundation/NSArray.h>
import“AddressCard.h”
@interface AddressBook:NSObject
{
NSString*bookName;
NSMutableArray*book;
}
-(id)initWithName:(NSString*)name;
-(void)addCard:(AddressCard*)theCard;
-(int)entries;
-(void)list;
-(void)dealloc;
@end
initWithName:方法设置了初始数组来存放地址卡片和簿的名称,而addCard:方法向簿添加AddressCard。entries方法报告簿中地址卡片的数量,而list方法给出簿中全部内容的简明列表。AddressBook类的实现文件参见代码清单15-10。
代码清单15-10 Addressbook.m实现文件
import“AddressBook.h”
@implementation AddressBook;
//set up the AddressBooks name and an empty book
-(id)initWithName:(NSString*)name
{
self=[super init];
if(self){
bookName=[[NSString alloc]initWithString:name];
book=[[NSMutableArray alloc]init];
}
return self;
}
-(void)addCard:(AddressCard*)theCard
{
[book addObject:theCard];
}
-(int)entries
{
return[book count];
}
-(void)list
{
NSLog(@“=======Contents of:%@=========”,bookName);
for(AddressCard*theCard in book)
NSLog(@“-20s%-32s”,[theCard.name UTF8String],
[theCard.email UTF8String]);
NSLog(@“=================================================”);
}
-(void)dealloc
{
[bookName release];
[book release];
[super dealloc];
}
@end
initWithName:方法首先调用超类的init执行初始化;然后创建一个字符串对象(使用alloc函数,这样它就拥有这个对象),并通过作为name传递,将地址簿名称设置为这个字符串;随后分配并初始化一个可变的空数组,用以存储实例变量book。
定义的initWithName:方法返回一个id对象而不是AddressBook对象。如果创建AddressBook的子类,那么initWithName:的参数不是AddressBook对象,它的类型是子类对象。因此,需将返回类型定义为一般对象类型。
还要注意在initWithName:方法中,通过使用alloc函数,可获得bookName和book实例变量的所有权。比如,使用NSMutableArray的array方法为book创建数组,见
book=[NSMutableArray array];
但你还不是book数组的拥有者,NSMutableArray拥有它,因此释放AddressBook对象的内存时,你无权释放它的内存。
addCard:方法获取作为参数提供给它的AddressCard对象,并将其添加到地址簿中。
count方法返回数组中的元素个数。entries方法使用这个方法返回存储在地址簿中的地址卡片数目。
15.4.1 快速枚举
list方法的for循环展示一个你以前从来没有见过的结构:
for(AddressCard*theCard in book)
NSLog(@“-20s%-32s”,[theCard.name UTF8String],
[theCard.email UTF8String]);
这里对book数组中的每个元素序列使用了一个被称为快速枚举的技术。它的语法非常简单:先定义一个能够依次保留数组中每个元素的变量(AddressCard*theCard)。使用关键字in跟随,然后列出数组的名称。当for循环执行时,它会为指定的变量分配数组的第一个元素并执行循环体。然后它会再为变量分配数组的第二个元素并执行循环体。这会一直持续,直到数组的所有元素都被分配给变量并且每一次都执行了循环体。
注意,如果theCard之前已经被定义为AddressCard对象,那么for循环将变得更加简单,如下:
for(theCard in book)
……
代码清单15-10是新AddressBook类的测试程序。</p>
代码清单15-10测试程序
import“AddressBook.h”
import<Foundation/NSAutoreleasePool.h>
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
NSString*aName=@“Julia Kochan”;
NSString*aEmail=@“jewls337@axlc.com”;
NSString*bName=@“Tony Iannino”;
NSString*bEmail=@“tony.iannino@techfitness.com”;
NSString*cName=@“Stephen Kochan”;
NSString*cEmail=@“steve@kochan-wood.com”;
NSString*dName=@“Jamie Baker”;
NSString*dEmail=@“jbaker@kochan-wood.com”;
AddressCard*card1=[[AddressCard alloc]init];
AddressCard*card2=[[AddressCard alloc]init];
AddressCard*card3=[[AddressCard alloc]init];
AddressCard*card4=[[AddressCard alloc]init];
AddressBook*myBook=[AddressBook alloc];
//First set up four address cards
[card1 setName:aName andEmail:aEmail];
[card2 setName:bName andEmail:bEmail];
[card3 setName:cName andEmail:cEmail];
[card4 setName:dName andEmail:dEmail];
//Now initialize the address book
myBook=[myBook initWithName:@“Lindas Address Book”];
NSLog(@“Entries in address book after creation:%i”,
[myBook entries]);
//Add some cards to the address book
[myBook addCard:card1];
[myBook addCard:card2];
[myBook addCard:card3];
[myBook addCard:card4];
NSLog(@“Entries in address book after adding cards:%i”,
[myBook entries]);
//List all the entries in the book now
[myBook list];
[card1 release];
[card2 release];
[card3 release];
[card4 release];
[myBook release];
[pool drain];
return 0;
}
代码清单15-10输出
Entries in address book after creation:0
Entries in address book after adding cards:4
========Contents of:Lindas Address Book=========
Julia Kochan jewls337@axlc.com
Tony Iannino tony.iannino@techfitness.com
Stephen Kochan steve@kochan-wood.com
Jamie Baker jbaker@kochan-wood.com
====================================================
这个程序设置了4个地址卡片,然后创建一个名为Lindas Address Book的新地址簿。随后使用addCard:方法在地址簿中添加4张卡片,list方法用于列出地址簿的内容并校验其内容。
在地址簿中查询某人
你有一本容量较大的地址簿,每次查询某人时不想列出簿中的所有内容,因此增加一个方法很有意义。我们将这种方法叫做lookup:,并将要查找的姓名作为参数。这个方法搜索整个地址簿来寻找匹配(忽略大小写),如果匹配成功,则返回该记录。如果电话簿中不存在所给的姓名,使其返回nil。
下面是新的lookup:方法。
//lookup address card by name—assumes an exact match
-(AddressCard)lookup:(NSString)theName
{
for(AddressCard*nextCard in book)
if([[nextCard name]caseInsensitiveCompare:theName]==NSOrderedSame)
return nextCard;
return nil;
}
如果将该方法声明放在接口文件中,而把定义放在实现文件中,则可以编写一个测试程序来试验这个新方法。代码清单15-11显示了这个程序,输出结果紧随其后。
代码清单15-11测试程序
import“AddressBook.h”
import<Foundation/NSAutoreleasePool.h>
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
NSString*aName=@“Julia Kochan”;
NSString*aEmail=@“jewls337@axlc.com”;
NSString*bName=@“Tony Iannino”;
NSString*bEmail=@“tony.iannino@techfitness.com”;
NSString*cName=@“Stephen Kochan”;
NSString*cEmail=@“steve@kochan-wood.com”;
NSString*dName=@“Jamie Baker”;
NSString*dEmail=@“jbaker@kochan-wood.com”;
AddressCard*card1=[[AddressCard alloc]init];
AddressCard*card2=[[AddressCard alloc]init];
AddressCard*card3=[[AddressCard alloc]init];
AddressCard*card4=[[AddressCard alloc]init];
AddressBook*myBook=[AddressBook alloc];
AddressCard*myCard;
//First set up four address cards
[card1 setName:aName andEmail:aEmail];
[card2 setName:bName andEmail:bEmail];
[card3 setName:cName andEmail:cEmail];
[card4 setName:dName andEmail:dEmail];
myBook=[myBook initWithName:@“Lindas Address Book”];
//Add some cards to the address book
[myBook addCard:card1];
[myBook addCard:card2];
[myBook addCard:card3];
[myBook addCard:card4];
//Look up a person by name
NSLog(@“Look up:Stephen Kochan”);
myCard=[myBook lookup:@“stephen kochan”];
if(myCard!=nil)
[myCard print];
else
NSLog(@“Not found!”);
//Try another lookup
NSLog(@“Lookup:Haibo Zhang”);
myCard=[myBook lookup:@“Haibo Zhang”];
if(myCard!=nil)
[myCard print];
else
NSLog(@“Not found!”);
[card1 release];
[card2 release];
[card3 release];
[card4 release];
[myBook release];
[pool drain];
return 0;
}
代码清单15-11输出
Lookup:Stephen Kochan
====================================
||
|Stephen Kochan|
|steve@kochan-wood.com|
||
||
||
|O O|
====================================
Lookup:Haibo Zhang
Not found!
通过lookup:方法在地址簿中找到Stephen Kochan(注意我们利用匹配时不区分大小写的事实),使用AddressCard的print方法显示所得的结果。第二次查询时,没有找到姓名Haibo Zhang,因此返回了上面的结果信息。
这个lookup消息非常简单,因为它必须找到整个name的精确匹配。更好的方法可以完成部分匹配,也可以处理多重匹配。比如记录Steve Kochan、Fred Stevens和steven levy都可以满足消息表达式
[myBook lookup:@“steve”]
的匹配条件。因为可能存在多重匹配,所以有效的方法可能是创建一个包含所有匹配的数组,并将该数组返回给方法的调用者(参见本章最后的练习2),如下:
matches=[myBook lookup:@“steve”];
从地址簿中删除某人
具有添加记录功能的地址簿管理程序如果没有删除记录的功能,那么它是不完整的。可以构造一个removeCard:方法从地址簿中删除特定的AddressCard。或创建一个remove:方法,它根据名字删除记录(见本章最后的练习6)。
因为已经对接口文件中做了几次改动,所以代码清单15-12再次显示包含新的removeCard:方法的接口文件。之后是新的removeCard:方法。
代码清单15-12 Addressbook.h接口文件
import<Foundation/NSArray.h>
import“AddressCard.h”
@interface AddressBook:NSObject
{
NSString*bookName;
NSMutableArray*book;
}
-(id)initWithName:(NSString*)name;
-(void)addCard:(AddressCard*)theCard;
-(void)removeCard:(AddressCard*)theCard;
-(AddressCard)lookup:(NSString)theName;
-(int)entries;
-(void)list;
@end
下面是新的removeCard:方法:
-(void)removeCard:(AddressCard*)theCard
{
[book removeObjectIdenticalTo:theCard];
}
关于什么是同一对象,我们的观点是占用同样的内存位置。所以,两个包含相同信息的地址卡片对象处于不同的内存单元时(比如,复制地址卡片对象时,就会出现这种情况),removeObjectIdenticalTo:方法并不把它们视为同一对象。
顺便提及,removeObjectIdenticalTo:方法用于删除和其参数相同的所有对象。但只有对象数组中多次出现一个对象时才会用到这个方法。
通过使用removeObject:方法,然后编写isEqual:方法用于测试两个对象的相等性可以使方法更完善。如果使用removeObject:方法,则系统会自动为数组中的每个元素调用isEqual:方法,同时提供要比较的两个元素。在这个例子中,因为你的地址簿包含AddressCard对象作为其成员,所以必须将isEqual:方法添加到该类中(应该重载从NSObject继承的方法)。该方法可以自己决定如何确定等同性。将相应的name字段和email字段比较,如果都相等,则可以从方法返回YES,否则,返回NO。你的方法可以这样写:
(BOOL)isEqual(AddressCard*)theCard
{
if([name isEqualToString:theCard.name]==YES&&
[email isEqualToString:theCard.email]==YES)
return YES;
else
return NO;
}
下面应该注意NSArray方法,如:containsObject:方法和indexOfObject:方法,都依赖isEqual:策略来决定两个对象是否相等。
代码清单15-12测试了新的removeCard:方法。
代码清单15-12测试程序
import“AddressBook.h”
import<Foundation/NSAutoreleasePool.h>
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
NSString*aName=@“Julia Kochan”;
NSString*aEmail=@“jewls337@axlc.com”;
NSString*bName=@“Tony Iannino”;
NSString*bEmail=@“tony.iannino@techfitness.com”;
NSString*cName=@“Stephen Kochan”;
NSString*cEmail=@“steve@kochan-wood.com”;
NSString*dName=@“Jamie Baker”;
NSString*dEmail=@“jbaker@kochan-wood.com”;
AddressCard*card1=[[AddressCard alloc]init];
AddressCard*card2=[[AddressCard alloc]init];
AddressCard*card3=[[AddressCard alloc]init];
AddressCard*card4=[[AddressCard alloc]init];
AddressBook*myBook=[AddressBook alloc];
AddressCard*myCard;
//First set up four address cards
[card1 setName:aName andEmail:aEmail];
[card2 setName:bName andEmail:bEmail];
[card3 setName:cName andEmail:cEmail];
[card4 setName:dName andEmail:dEmail];
myBook=[myBook initWithName:@“Lindas Address Book”];
//Add some cards to the address book
[myBook addCard:card1];
[myBook addCard:card2];
[myBook addCard:card3];
[myBook addCard:card4];
//Look up a person by name
NSLog(@“Lookup:Stephen Kochan”);
myCard=[myBook lookup:@“Stephen Kochan”];
if(myCard!=nil)
[myCard print];
else
NSLog(@“Not found!”);
//Now remove the entry from the phone book
[myBook removeCard:myCard];
[myBook list];//verify its gone
[card1 release];
[card2 release];
[card3 release];
[card4 release];
[myBook release];
[pool drain];
return 0;
}
代码清单15-12输出
Lookup:Stephen Kochan
====================================
||
|Stephen Kochan|
|steve@kochan-wood.com|
||
||
||
|O O|
====================================
========Contents of:Lindas Address Book=========
Julia Kochan jewls337@axlc.com
Tony Iannino tony.iannino@techfitness.com
Jamie Baker jbaker@kochan-wood.com
===================================================
在地址簿中查询Stephen Kochan并检测出它在簿中后,将所得结果AddressCard传给新removeCard:方法来删除它。结果地址簿列表验证了删除成功。
[1]你可能还想有一个名为initWithName:andEmail:的初始化方法,但在这里不显示这个方法。