iPhone学习(4)----面向对象

来源:互联网 发布:网络用语wm是什么意思 编辑:程序博客网 时间:2024/05/26 20:21

定制类


设计阶段

     创建一个类

            Person

   确定其父类

           NSObject(在本例中)

   确定其具有的属性

            Name,age,是否可以投票

   确定其行为

       发起投票


     创建一个类时,你会得到两个文件.h.m文件,.h文件是头文件,.m是实现文件

    在头文件中声明一个类(@interface开头,@end结尾,@interface后接类名,若有父类,则冒号后接父类名,因为NSObject来自与Foundation类集,所以需要导入Foundation框架集,在这里用import而不是include,原因为Objective-C中的微小差别,#import会自动探测来确保你不会包含相同的头文件两次,用大括号去指明实例变量,在这里需要注意的是gettersetter,你的getter会一直返回你返回的对象的类型或你返回变量的类型。在setter中,你无返回值)

import<Foundation/Foundation.h>

@interfacePersonNSObject

{

//实例变量

NSString* name;

intage;

}

//方法的声明

-(NSString*)name;

-(void)setName:(NSString*)value;

-(int)age;

-(void)setAge:(int)value;

-(BOOL)canLegallyVote;

-(void)castBallot;

@end


实现文件

实现文件会包含setter,getter功能,以及之前定义的行为功能,这里是castBallot,首先需要导入我们自己定义的类结构,然后是以@implementation开头,@end结尾,在@implementation后接类名。在实现文件中,如果需要调用自身类中的方法,你可以使用self。如果你不需要进入哪个实例变量,就不要调用settergetter,它帮助你在需要的时候局部化某些代码。

import“Person.h”

@implementation Person

-(int)age;

-(void)setAge:(int)value;

//。。。。。。其他方法

-(BOOL)canLegallyVote

return([self age]>=18);

-(void)castBallot;

if([selfcanLegallyVote])

{

//do voting stuff

}else

{

NSLog(@”我不被允许投票!”);

}


@end


超类方法

如果你要重写在超类中已经实现的方法,你可以去调用超类的那个方法,使用关键字super可以调用父类中的方法。

例如:

-(void)doSomething{

[superdoSomething];

intfoo=bar;

//。。。。

}


在调用实例变量时,不需要使用self关键字,这个惯例是在你不需要直接地进入变量时使用settergetter。在调用功能时不能不要self关键字,在Objective-C中不可以,你需要self来使它能够通过存取和方法,如果你不用self,它会为这个变量取实际的内存。


对象的生命周期

对象的创建

当你在Cocoa中分配一个对象,通常你会做两件事,你会做分配和初始化,分配事找到一个存储区,它会为类创建一个存储区,然后你调用它,init会配置好内存,这样它就准备好了。

+alloc

alloc是一个类功能,以person为例子,我们会调用Personalloc,这样会创建一个足够大的存储区去保存PersonNSObject需要的所有内存。

-init

实例方法,在这个方法中我们可以做一些初始化。NSObjectinit实际上会为你检查和清理所有的内存,所有的东西都是零。

创建=分配+初始化

例子:

Person*person=nil;//赋值零,这样我们就清空了它

person=[[Personalloc] init];


实现你自己的init方法

import“Person.h”

@implementation Person

-(id)init

{

if(self=[superinit])//我们需要确认调用了NSObjectinit,所有东西都清理并正确地设置,然后把它分配给self,并且确保返回来的self不是nil,如果返回值为nil,则是发生了严重的错误,可能是内存无法初始化,可能会发生很糟糕的事情。

{

age=0;

name=@”Bob”;

}

returnself;

}

@end


多个init方法

cocoa中的Objective-C中还有另外一个惯例,你可能在一个对象上有多个init方法,init一直是基本的一个,但是它们可能会变得很复杂。简单的总是被复杂的所调用,所以,基本的init方法会被带着默认值initWithName所调用,而initWithName会被带着默认值的initWithName: age所调用,我们在这里有一个叫做指定初始化程序的initWithName: age,通常情况下,当一个子类想要重载它超类的所有的行为时,它就会重载指定初始化程序,initWithName可以设置name,也可以设置age,当它调用它的super,但是这个super没有指定初始化程序,它会调用init

例如:

-(id)int;

-(id)initWithName:(NSString*)name;

-(id)initWithName:(NSString*)name age:(int )age;


对象的总结

Person*person=nil;

person=[[Personalloc] init];

[personsetName:@”jimmy”];

[personsetAge:32];

[personcastBallot];

[persondoSomethingElse];



内存管理


Objective-C中,你不会直接调用dealloc


引用计数

NSObject会负责计算对象被使用的次数,它采用两种功能,一种是retain,另一个是release,所有的对象都有被NSObject定义的count,只要对象的retaincount大于零,这个对象就不会被dealloc。只要retaincount变为零,这个内存也会清除。默认情况下,+alloc和-copy总是创建retaincount1的对象,-retain使retaincount增加1,-release使retaincount1,你release的总数匹配retainalloccopy的总数,当retaincount变为零,dealloc会被自动调用,当你在dealloc状态,这表明你的内存将被清除,这个使不可逆的。


匹配回调

Person*person=nil;

person=[[Personalloc] init];

[personsetName:@”jimmy”];

[personsetAge:32];

[personcastBallot];

[persondoSomethingElse];

[personrelease];

如果你发信息给一个已经dealloc的对象,会发生崩溃。有个惯例是,一旦你release什么,设置那个变量的本地实例为nil,然后就是当你给它任何消息时,它都不会理会。


实现一个dealloc方法

import“Person.h”

@implementation Person

-(void)dealloc

{

//dosomething release

[namerelease];//假设该对象有唯一引用,确保在此处变为0

[superdealloc];

}


@end

对于传递的对象,谁传递对象进来,他本身负责匹配releaseretain.也就是说有人分配了对象,传递它给我们,我们把事情做完后,我们会retainrelease它,我们的count对于我们来说净值为0,原来的那个仍然需要释放它,才能解除这个对象的分配。当你有一个Setter方法时,你也得保证在dealloc方法中被释放。


回到实现文件

import“Person.h”

@implementation Person

-(NSString*)name{

returnname;

}

-(void)setName:(NSString*)newName{

if(name!=newName)//检查传递进来的数值是否与已经存在的相同

{

[namerelease];

name=[newNameretain];//不用name=newName是因为我们像保持一个对它的引用,它可能是某人传递给我们的。这里还可以写成name=[newNamecopy];//如果某个人想要改变名字,这里有NSStringNSMutableString,如果有人传进它们可以改变NSMutableString,如果有人对它做了某些行动,然后你copy它,你会保留传给你的名字,它不会改变。

}

}

@end

objective-C中,因为所有的东西都是对象的一个指针,不需要担心指针和静态实例。





Autorelease

返回一个新创建的对象

-(NSString*)fullName{

NSString*result=[[NSString alloc] initWithFormat:@”%@%@”,firstName,lastName];

[resultautorelease];

returnresult;

}

autorelease实际做的是与其立刻减少retaincount,还不如先将其放到一个堆栈上,然后迟点再释放。autorelease标记了对象,因此它会再未来的某时被调用和释放,这让你确保你的类在匹配retainrelease是正确的。allocautorelease是匹配的。当需要创建一个对象和返回它而不需要担心谁调用它,调用它做什么。


方法名称和autorelease

如果你调用了一个以alloccopynew开头的方法,它会返回一个对象的非autorelease的实例,因此你需要负责减少retaincount。当看到一个方法是以string开头,它已经预先为你autorelease

Autorelease是如何工作的

当你调用autorelease,它会增加一个叫autorelease池的东西。当对象要预定被release时,基本上到这个池逝去时,池中对象会反复被release.当你在autorelease池中调用drain,这会检查池中所有的对象,并逐个在它们上面调用release。实际上,这是它们可能调用dealloc,或者它们可能不调用deallocautorelease池,每个运行循环,每个事件自动排干autorelease池,这个就是生命周期。当你开始你的应用程序,它在你启动iphone上的app时开始,它初始化这个应用然后载入主要的nib文件,这是你应用的间期,然后它开始贸易个运行循环,这个方法的基本是检查应用的收入。查看用户的任何动作,当用户有一些动作时,它运行到收听模式,因此它会处理事件,这个事件,它会创建这个autorelease池,这个事件可能会创建一群对象,我们创建一个NSString,然后这个对象进入池中,假设在同一个时间,你在完成前做了很多处理,你创建另外一个对象,你在那个对象上调用autorelease,它进去了池中,你在另外一个对象上用了autorelease。它也进去池中,当你完成了这件事的循环,你就完成了所有的处理。这个池被清干,每个对象都会被调用release。当你完成时,这件事循环也结束了。所有你的autorelease对象会被释放。如果retaincount0,那么就会被dealloc。如果你想保留一个被autorelease的对象,你可以增加retaincount数字。假设你创建一个NSString,在它那里调用了autorelease,类似叫NSStringstringWithFormat,但是你想保留它久一点,你在它那调用retain,我们分配给它一个setter,因此当我们在它上面调用setName,它会保留这个对象,然后autorelease发生,retaincount降为1,它不会被dealloc,然后当我们解除person对象的分配,这时我们会在Name上减少retaincount,它会被dealloc


垃圾回收

autorelease不是垃圾回收

objective-C中的iphoneos没有垃圾回收,但是在mac中有。




属性

在属性中,你可以指定属性是否为只读或者为读/写,也可以指定这个内存应该retaincopy或者只需要assign

定义属性

import<Foundation/Foundation.h>

@interfacePersonNSObject

{

//实例变量

NSString* name;

intage;

}

//方法的声明

-(NSString*)name;

-(void)setName:(NSString*)value;

-(int)age;

-(void)setAge:(int)value;

-(BOOL)canLegallyVote;

-(void)castBallot;

@end

上面是Person类的声明,有NamesetNameagesetAge,甚至还有这个canLegallyVote,它返回一个布尔值,我们可以取这5个方法,然后减为三个属性。如下所示

@propertyint age;

@property(copy)NSString *name;//内存管理为copy

@property(readonly)BOOL canLegallyVote;//只读表明它不会为你生成setter,只生成getter


属性的实现


import“Person.h”

@implementation Person

-(int)age

{

returnage;

}

-(void)setAge:(int)value

age=value;

-(NSString*)name{

returnname;

}

-(void)setName:(NSString*)value{

if(name!=value)//检查传递进来的数值是否与已经存在的相同

{

[namerelease];

name=[valueretain];

}

@end

可以写成

import“Person.h”

@implementation Person

@synthesizeage;

@synthesizename;

-(BOOL)canLegallyVote

{

return(age>17)

}

@end


上面做的是它自动基于你在声明中设置的所有属性,为你生成settergetter



内存管理属性

@property(assign)NSString *name;

@property(retain)NSString *name;

@property(copy)NSString *name;

assign的意思是当它创建setter时,它不会保持新的对象,它不会释放旧的对象。它只会分配它。retain,它会释放旧对象和保持新对象,copy做的也是一样,它会释放旧的对象和复制新的。


属性名字和实例变量

@interfacePerson:NSObject{

intnumberOf YearOld;

}

@propertyint age;

@end


@implementationPerson

@synthesizeage= numberOf YearOld

@end

属性名可以与实例变量不同,你告诉编译器去为你生成settergetter的方法是当你合成时,你与其说”synthesizeage:”不如说“synthesizeage=实例变量的名字”,你可以做的另外一件事就是即使synthesize会为你生成settergetter,但是你仍然可以实现自己的settergetter方法。

例如:

@implementationPerson

@synthesizeage

@synthesizename;

-(void)setAge:(int)value

{

age=value;

//可以做其他事情

}

@end

在上面例子中,getter功能仍然被合成了,但是我们的setter被重写了。实际上,属性是在objective-C2.0中被引入的,属性在后台可以帮你做做很多东西。


.(点)语法和self

一个对象对自身对象使用点号语法,做这个的时候你必须清晰,属性和实例变量

@interfacePerson:NSObject

{

NSString*name;

}

@property(copy)NSString *name;

@end


在一些语言中,如果你说self.age=age;你实际上进入了age实例变量内存的偏移量,在这个例子,如果你说self.age,实际上你在调用setAge方法

例如:

@implementationPerson

-(void)doSomething{

name=@”Fred”;//直接进入实例变量

self.name=@”Fred”;//它会调用合成的selfName方法。这样会retain新值,并且会释放旧值

}


@implementationPerson

-(voidsetAge:(int)newAge

{

self.age=newAge;//死循环,这代码相当与[selfsetAge:newAge];

}

@end



原创粉丝点击