前言
在实际的开发过程中,继承和类别都会得到很多用处。对于界面相似度很高的情况下,使用继承可以节省很多代码和设置,只需要在子类中重写父类中的方法,或者增加新的子类方法即可,代码非常的简洁维护起来也很方便。下面小节下相关的知识,供需要的朋友查看。 在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字段,那么需要遵循 <NSCopying>协议,并且需要重写 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.静态方法/类方法
静态方法在堆上分配内存(释放工作由程序员控制),实例方法在栈上(是由编译器自动管理)
静态方法常驻内存,实例方法不是,所以静态方法效率高但占内存
本文参考文章: