基础篇-ObjectC继承、类别、属性


基础篇-ObjectC继承、类别、属性_第1张图片

前言

    在实际的开发过程中,继承和类别都会得到很多用处。对于界面相似度很高的情况下,使用继承可以节省很多代码和设置,只需要在子类中重写父类中的方法,或者增加新的子类方法即可,代码非常的简洁维护起来也很方便。下面小节下相关的知识,供需要的朋友查看。    在Objective-C中,给一个类扩展一个其它方法,有两种实现方式:类别和继承。


 继承

 这个是面向对象语言都有的一个特性,子类会继承父类的方法属性以及成员变量。  这里说的方法需要在 父类中的 .h中声明,子类才可以使用super 调用父类的方法,可以继承过来父类的一切属性,可以使用父类的成员变量。

    .h 文件相当于一个对外公开的 head ,是因为 oc 中无法导入 .m 文件,只能导入.h 文件,所有子类中需要用到父类中导入的头文件的话,这个头文件需要在 父类的.h  中导入,子类无法调用 父类 .m 中导入的头文件 和声明的 成员变量。所以把 .m 文件中的东西相当于是 私有的,不会被非本类的对象所调用。

     在继承中需要注意的是:重写的这个方法在父类中执行时会替换掉原来的方法的(就算子类中没有调用这个新重写的方法这个新方法也已经被执行了),一般自己不调用这个重写的方法,子类一般只调用新加的方法。

  在 .h 中声明成员变量,又不想被子类调用,可以对这个成员变量进行限定如:

@private

NSMutableArray*modelArray;

@private 私有成员,只有当前类可以访问;

@protected 受保护成员,只有当前类或子类可以访问(如果没有添加任何修饰则默认为@protected);

@public 公共成员,所有类均可访问;


类别category

这是Objective-C语言的一个特性,可以在不改变类名和原来类的实现的前提下,实现对类的方法扩展。

以下两种方式最好使用类别。

1)针对系统提供的一些类,例如:NSString,NSArray,NSNumber等类,进行方法扩充的时候。

2)类别支持开发人员针对自己构建的类,把相关的方法分组到多个单独的文件中,对于大型而复杂的类,这有助于提高可维护性,并简化单个源文件的管理。

对于以下情况,无法使用类别,必须使用继承。

1)新扩展的方法与原方法同名,但是还需要使用父类的实现。因为使用类别,会覆盖原类的实现(继承也会覆盖,就是所谓的重写,但是可以在重写的时候调用  父类的同名方法,而类别不能),无法访问到原来的方法。

2)扩展类的属性,这个类别无法做到。

OC中的子类可以拥有和父类相同名称的方法,在子类调用时,优先去自己的内部寻找,如果没有则一层一层的往上找;

(4)OC语言是单继承语言。在OC语言中,基本上所有类的根类都是NSObject类。

提示:重写即子类重新实现了父类中的某个方法,覆盖了父类以前的实现。

提示:每个类中都有一个super class指针,该指针指向自己的父类。对象中有一个isa指针,该指针指向调用该对象的类。

继承的好处:

(1)抽取出了重复的代码

(2)建立了类和类之间的联系

继承的缺点:耦合性太强

属性

在OC中定义变量,可以自己来定义变量的setter方法来设置变量值,用getter方法来获取变量值,但是当变量数量增多时,还采用手动添加setter/getter方法来操作变量,就会使得程序代码量大大增加,于是就出现了 @property 来快速声明设置获取变量的值的方法,这也许就是  @property @synthesize/@dynamic 设计的本意。

@property是给编辑器看的。就算你不声明@property,在obj的@implenmention下写好valueA和setValueA,还是可以obj.valueA赋值或取值,但是没有自动联想。

只声明@property而不去实现,在Xcode4.4以后会自动帮你生成get和set方法

本质上来讲,属性也会帮你定义一个成员变量,并根据属性的声明自动生成getter/setter 方法,其中setter 方法根据属性(property)的属性(attribute)来提供不同的内存管理策略。

@property是一个属性访问声明以及声明getter,setter方法

扩号内支持以下几个属性:(getter=getterName,setter=setterName,设置setter与getter的方法名,retain,copy,assign.......)

在声明property属性后,有2种实现选择

@synthesize  作用是实现属性的,如getter,setter方法. (通过类别和runtime 的对相关联技术生成新的属性时,无法使用这个这个设置,只能使用@dynamic)

编译器期间,让编译器自动生成getter/setter方法。

当有自定义的存或取方法时,自定义会屏蔽自动生成该方法

@dynamic

告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告

然后由自己实现存取方法

如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;

我们来看一下属性的本质和进化过程:

原始形态

    定义一个实例变量:int age;

    先在.h文件中声明setter和getter器

    -(void)setAge:(int)newAge;

     -(int)age;

然后在.m文件中具体实现

-(void)setAge:(int)newAge

{

     age=newAge;

}

-(int)age

{

 return age;

}

这样的话就可以跟属性一样使用了。

//比如上面的声明是一个Person类

Person*person=[[Person alloc]init];

[person setAge:13];

int age=[person age];

//点调用

person.age=13;  //.调用出现在=号左边,相当于setter

intage=person.age   //.调用出现在=号的右边,相当于getter

NSLog(@"%i",person.age);//这也是getter


setter和getter的改进写法:

每次要为一个属性写上getter和setter,不得不手十分麻烦,所以有了更简单的写法,

在.h文件里,直接这样写,表示声明了一个实例属性和它的getter和setter器

@property int age;

然后在.m文件中这样写,

@synthesize age;

表示实现setteer和getter,这样,就可以和以前一样调用getter和setter了。


setter和getter的改进优化:

可以看到,getter器的方法名直接就是变量名,方法名和变量名一样,容易让人迷糊,所以,可以这样优化。

在.h文件中依然这样声明

@property int age;

在.m文件中,这样去写,

@synthesizeage=_age; //加上一个_

//这么,我们就可以去使用_age   和使用age一样

-(void)show

{

NSLog(@"%i",_age);

}

可以看出来,在Objective-C中setter器没什么区别,不过getter器的方法名缺少了get,因为get...在Objective-C有别的用处,所以getter器直接写的就是变量名。

     1. 如果只声明一个属性a,不使用@synthesize实现:编译器会使用_a作为属性的成员变量(如果没有定义成员变量_a则会自动生成一个私有的成员变量_a;如果已经定义了成员变量_a则使用自定义的成员变量_a。注意:如果此时定义的成员变量不是_a而是a则此时会自动生成一个成员变量_a,它跟自定义成员变量a没有任何关系);

      2.如果声明了一个属性a,使用@synthesize a进行实现,但是实现过程中没有指定使用的成员变量(例如上面birthday):则此时编译器会使用a作为属性的成员变量(如果定义了成员变量a,则使用自定义成员变量;如果此时没有定义则会自动生成一个私有的成员变量a,注意如果此时定义的是_a则它跟生成的a成员变量没有任何关系);

      3.如果声明了一个属性a,使用@synthesize a=_a进行实现,这个过程已经指定了使用的成员变量:此时会使用指定的成员变量作为属性变量;(那还不如不写这个 @synthesize ..)

有了上面的总结,相信理解上面的代码并不难,通常在实际开发过程中我们要么直接在@property中声明不使用@synthesize;要么使用过程中指定具体的成员变量。

此外再次强调一下,通过上面的方式定义变量的本质还是生成对应的gettter、setter方法(只是这个步骤编译器帮你完成了),如果通过@property定义了属性,同时在.m中又自定义实现了对应方法,则会使用自定义方法。


定义setter的语义

nonatomic  禁止多线程,变量保护,提高性能。

atomic    是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种

机制是耗费系统资源的

assign   此标记说明设置器直接进行赋值 ,赋值特性,不涉及引用计数,弱引用对基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float,double, char)等等。

如果你要一个属性使用assign,且这个类符合NSCopying协议.

retain    对其他NSObject和其子类对参数进行release旧值,再retain新值,新对象的引用计数+1;

指定retain会在赋值时唤醒传入值的retain消息。此属性只能用于Objective-C对象类型,而不能用于Core

Foundation对象。(原因很明显,retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数——译者注)。

注意: 把对象添加到数组中时,引用计数将增加对象的引用次数+1。

copy   对NSString 它指出,在赋值时使用传入值的一份拷贝。拷贝工作由copy方法执行,此属性只对那些实行NSCopying协议的对象类型有效,表示两个对象内容相同,新的对象retain为1 ,与旧有对象的引用计数无关。

weak    weak比assign多了一个功能,当对象消失后自动把指针变成nil,好处不言而喻。弱引用除了不决定对象的存亡外,其他与强引用相同。即使一个对象被持有无数个若引用,只要没有强引用指向他,那麽其还是会被清除。

retain   是指针拷贝,copy 是内容拷贝。

iOS 5 中对属性的设置新增了strong 和weak关键字来修饰属性(iOS 5 之前不支持ARC)

strong   用来修饰强引用的属性,一块内存(一个对象)当没有 strong 类型的指针指向它时,它就会被释放;ARC中使用,与MRC中retain同义,使用之后,计数器+1。

官方文档中有这样的示例代码:// The following declaration is a synonym for: @property(retain) MyClass *myObject;@property(strong) MyClass *myObject;表示了strong和retain是同义词。

weak    用来修饰弱引用的属性,当一块内存(一个对象)被释放时,指向它的 weak 类型指针就会被释放并赋值为 nil。

所以,如果一般情况下,我们都不希望字串的值跟着str变化,所以我们一般用 copy 来设置string的属性。如果希望字串的值跟着赋值的字串的值变化,可以使用 strong,retain。当然这也只是针对 NSMutableString,因为如果是 NSString 那么 copy 与 retain 的效果是一样的。

这里需要提一下,如果一个对象作为属性时,定义setter的语义使用了copy字段,那么需要遵循 协议,并且需要重写 copyWithZone 方法。否则会cash。

NSZone 是苹果对内存分配和释放的优化方式。NSZone不是一个对象;它是一个难懂的C结构,它被用于纪录关于内存处理(管理)一系列对象的信息。

- (id)copyWithZone:(NSZone*)zone

{

    id   copy = [[[self  class]alloc]init];

   if(copy){

    // self.imageName 是 当前的属性

     [copy   setImageName:[self.imageName copyWithZone:zone]];

   }

       return   copy;

}

一些需要注意的知识

1.实例方法/动态方法

2.静态方法/类方法

静态方法在堆上分配内存(释放工作由程序员控制),实例方法在栈上(是由编译器自动管理

静态方法常驻内存,实例方法不是,所以静态方法效率高但占内存



本文参考文章:

OC基础相关

属性相关

你可能感兴趣的:(基础篇-ObjectC继承、类别、属性)