---------------------- Java培训、.Net培训、Android培训、IOS培训、期待与您交流! ----------------------
(1) 继承是什么
面向对象语言继承语法是比不可少的。那么日常生活中的继承意思就是把父一代的所有东西保留下来。在OC中继承也是这个意思,不过类之间的继承是把父类的属性和方法都继承下来,另外子类再有选择地扩展自己的功能。
继承在OC中无处不在,我们知道创建一个OC对象必须继承与NSObject,因为NSObject是OC面向对象的特性的基类。
OC中只支持单继承,并不像C++那样可以继承多个父类。
(2) 继承简单使用
#import <Foundation/Foundation.h> @interface Person : NSObject // 冒号是继承语法的关键符号 // Person是NSObject的子类(subclass),NSObject是Person的父类(superclass) code ...; @end; /* OC只支持单继承,因此以下写法错误 @interface GoodStudent : Person ,Student ..code..; @end */
(3) 继承的利弊
1) 好处
继承带来的最大的好处是代码可重复利用,并可以根据需要扩充方法。
建立了类之间的联系,有利于提高代码的可读性(在考虑类之间使用组合关系还是继承关系要看类之间的逻辑关系是什么)。
有利于公共代码的抽取。如果某一些类他有很多种形态,但他们都有共同的特征,那么这时候就可以用继承关系把他们的共同特征抽取到父类中,遇到特殊需求要更改共同特征只需要把父类该了就行了。
2) 坏处
两个类之间的耦合性增强了。如上例,如果再增加一个Student类继承于Person那么Student就不能离开Person类而独立存在。
(4) 继承注意点 :
1) 子类的属性名称不能跟父类的属性名称相同
2) 子类如果有跟父类一样的方法子类的方法会覆盖父类的方法,在对象调用该方法的时候会调用子类的方法。
(1) 在其他面向对象语言中谈到继承我们很容易就联想到多态这个概念:某一种事物具有多种形态。
(2) 多态利用父类类型指针调用子类方法的过程其实是一种动态绑定的过程 : 在运行时根据对象的类型确定动态调用的方法
(3) 好处 : 节省代码,如果某一个方法中接收的参数类型要求是具有某种特征的类的话使用多态特性可以一劳永逸
(3) 坏处 : 不能利用父类指针来访问子类属性。
//////////////////////Person类/////////////////////////////////////////////// // Person.h #import <Foundation/Foundation.h> @interface Person : NSObject { int _age; } setter getter ...; @end; // Person.m #import "Person.h" @implementation Person setter getter.. @end //////////////////////Student类/////////////////////////////////////////////// #import "Student.h" @interface Student: Person { NSString *_name; } setter getter ...; @end; // Student.m #import "Student.h" @implementation Person setter getter.. @end //////////////////////main/////////////////////////////////////////////// int main(){ @autoreleasepool{ Person *p = [[[Student alloc] init] autorelease]; // 多态就是可以用父类类型指针指向子类对象,并可以用父类指针调用子类方法 [p setAge:22]; // 在调用过程中只关心指针在内存中指向的是什么对象,p指向Student对象,Student继承与Perosn拥有Person类的方法 [p setName:@"mike"]; // 多态可以利用父类类型指针调用子类的方法,因为在内存中该指针指向的是Student对象 } return 0; }
(1) 一个类的实例化我们通常都是利用多态来调用NSObject的alloc和ini方法打开NSObject的头文件发现init返回的是id类型的数据。
(2) 在OC中我们知道id是一个可以指向任何对象的万能指针,那么他具体如何声明?
打开源码 :
struct objc_object { Class isa OBJC_ISA_AVAILABILITY; } *id;
(3) 通过发现我们知道对象的本质一个结构体。
(4) 那么这个id类型有什么用?
1) 既然OC有多态的特性那么,在多态中可以使用id类的变量作为方法的参数
2) 为了更好的结合id类型发挥多态的好处,通常会使用协议(后面会详细讨论)这个特性来限制id的方法。
(1) OC中构造方法的概念
在java中构造方法是实例化一个对象的方法,而在OC中是由[NSObject new] == [[NSObject alloc] init]引申出来的一种新概念 : 就是重写 init 方法来完成对象初始化,但要注意的是重写方法中一定要调用父类的init方法:[super init]。
(2) 构造方法的命名规范
1) 构造方法是类方法
2) 为了好区分构造方法和完善继承的问题,构造方法 命名规范 和 方法体定义如下 :
+ (id) initWith:(X)x and:(Y)y { id obj = [[super alloc] init]; // 一定要调用父类的初始化方法 [obj setX:x]; [obj setY:y]; return obj; }
(3) 使用构造方法的好处 : 有了构造方法我们就可以在构造方法中完成对象的初始化和属性的赋值,在调用过程中简化代码。
oc对象方法的调用是基于消息机制的,那么这种消息究竟是怎么样的?打开源码我们可以得到SEL的原型
// SEL的原型 typedef struct objc_selector *SEL;
SEL在类中无处不在,我们可以通过在类的方法体内通过 _cmd 来获得该方法的SEL指针:
@implementation Person - (int)age { SEL getter = _cmd; return _age; } @end
(1) 猜测
我们知道OC是一种建立在C语言基础指向的面向对象语法,调用对象方法的关键符号是 [obj sel]; 我的猜测是OC已经对 [ ] 进过过运算符的重载了,然后通过该重载函数实现消息机制。
(2) 调用方法原理 :
[obj sel]; // 调用过程分两步:
1) 先吧方法名和参数包装成SEL,然后通过该对象的isa指针发送消息调用相应的方法。
(3) 调用的细节过程 : 方法调用的原理:其实每个类加载到内存中对象的方法都会封装成一系列的SEL列表,SEL会保存着这个方法的信息(当然包括方法的内存地址),当要调用该方法的时候,对象会通过isa指针找到内存中对应的 方法的内存地址然后再调用该方法,在找到该方法之后该类对象会把该SEL放入缓存中,以便第二次寻找的时候提高效率
OC为了使其他语言的程序员快速入门提供的点语法特性
(1) 点语法其实是对象的setter 和 getter的替代
(2) 点语法是如何区分调用的是setter还是getter :
/* 区分的最好方法是看是否有返回值: 需要返回值 --> getter 需要参数 --> setter */ Person *p = [[Person alloc] init]; p.age = 20; // setter == [p setAge:20] int age = p.age; // getter == int age = [p age]; NSLog(@"age is %d",p.age); // getter
点语法调用的方法跟属性名称无关只关心方法名
@interface Person : NSObject - (void) setAge:(int)age; // p.age = 20; 如果 - (void) setAgeless:(int)age; p.ageless = 20 - (int) age; // int age = p.age; 如果 - (int) ageless; int age = p.ageless; - (void) setName:(NSString *)name; // p.name = @"mike"; - (NSString *)name; // NSString *name =p.age; @end
(1) 我们知道一个对象创建通常都是由以下语句产生 : Object obj = [[Object alloc] init];从表达式可以看到对象产生过程大概经历2步 (a) 内存空间分配 (2) 类的初始化
(2) 从上述表达式对象是由类的类方法产生的。
(3) 对象的属性和方法是如何存放 :
1) 每个对象在产生的时候都会分配一个 isa(Class类型) 指针指向类对象
2) 对象的属性跟对象放在同一块内存空间内
3) 对象的方法存放在类对象里
(1) 首先会把方法封装成SEL发送给类对象,
(2) 类对象会根据该SEL找到相应的SEL列表然后遍历该列表然后找到该方法内存地址
(3) 然后把该方法内存地址放进缓存里,以便下一次调用提高效率
(4) 调用该方法返回相应的值
既然对象是由类对象产生的,那么类对象就存在于内存中,那么类对象是什么时候加载到内存中:
(1) 在程序启动过程中会把所有类加载以便调用该类的 +load 方法(先调用父类的+laod方法,然后再调用子类的)而且 只会调用一次, 用来监听类的加载完毕 ------ 在程序启动时候执行的
(2) 我们可以控制他们的加载顺序 : Build Phases --> Compile Sources 改变文件的顺序来改变加载顺序
(3) 在类的第一使用时候会回调他的 +initialize 方法,而且先调用父类的 +initialize 方法 再调用子类的 +initialize -------- 在类的第一使用的时候执行
(4) 我们可以利用 +load 和 +initialize 方法来完成一些我们想做得事。
description方法是用来方便与NSLog方法来输出类个对象的描述,它使用的占位符为 %@
这个方法是用来输出对 类 和 对象 的描述信息,我们可以重写该方法 :
// Person.m #import "Person.h" @implementaion Person + (NSSting *) description { return @"这是一个Person类"; } - (NSString *) description { return @"这是一个Person对象"; } @end // main.m #import "Person.h" int main() { Person *p = [[Person alloc] init]; NSLog(@"%@",p); // 这是一个Person对象 NSLog(@"%@",[p class]); // 这是一个Person类 return 0; }
---------------------- Java培训、.Net培训、Android培训、IOS培训、期待与您交流! ----------------------
详情请查看:http://edu.csdn.net/heima