您的位置:首页 > 职场人生

黑马程序员--07 OC 三大特性

2015-03-03 22:40 337 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流!
-------

三大特性:封装,继承,多态

#封装 #

@public的成员可以被任意赋值,这样数据通过指针->成员变量名 方式给成员变量赋值,可能被赋值为不合理的数值具有危险性。通过封装后便可解决上述问题,封装的好处:过滤不合理的值,保证数据的安全性; 屏蔽内部的赋值过程;让外界不必关注内部的细节

不使用@public后,便不能通过指针->成员变量名 的方式给成员变量赋值 (被保护)。要通过调用方法来设置成员变量的值。
设置成员变量属性值的方法, 通常称为该属性的set方法
set方法:
作用:提供一个方法给外界设置成员变量的值,可以在方法里面对参数进行相应的过滤。

set方法命名规范: a.方法名必须以set开始

b. set后面跟上成员变量名称,其首字母必须大写

c.返回值一定是void

d.一定要接收一个参数,而且参数类型和成员变量类型一致

e.形参名称不能和成员变量名称一样

如 - (void) setWheels: (int)wheels;

没有@public, 我们就不能通过 指针->成员变量名 来查看成员变量的值 (被保护). 我们要通过调用方法来返回成员变量值

获取成员变量值得方法,通常称为get方法。

get方法:

作用:返回对象内部的成员变量

get方法命名规范

a.肯定有返回值,返回值类型和成员变量类型一致

b.方法名与一般与成员变量名一样(不以get开头;不加下划线)

c.不需要接收任何参数

如 - (int) wheels;

成员变量命名规范

a.一定要以下划线开头

作用:a.让成员变量与get方法区分开

b.可以与局部变量区分开,一看到下划线开头的变量一般是成员变量

#import <Foundation/Foundation.h>

@interface Student : NSObject
{
int _age;
}

- (void)setAge:(int)Myage;
- (int)age;

@end

@implementation Student

- (void)setAge:(int)Myage
{
_age = Myage;
}

- (int)age
{
return _age;
}

@end

int main()
{
Student *p = [Student new];

[p setAge:10];

NSLog(@"p=%d",[p age]);

return 0;
}


#继承#
继承的含义

类B继承了类A,那么类B具有类A的所有属性和方法.

在OC中继承关系用 :表示.
继承的好处:

a.抽取重复代码

b.建立类之间的关系

c.子类可以拥有父类的所有成员变量和方法

e.不改变原来模型的基础上,拓充方法

缺点:耦合性太强

细节:OC是单继承,每个类只能继承一个父类,不能继承多个.
NSObject是基类,几乎所有的类最终都是继承于它.也有的类不是继承NSObject的,比如NSProxy,它也是
一个基类.我们常用NSObject类.
父类\超类 superclass 子类
subclass\subclasses

子类有自己的成员变量方法,和父类的所有成员变量和方法 (以及父类的父类的).
#子类对父类的重写#

注:

父类必须声明在子类的前面. 实现可以放在后面.和函数一样.

子类不可以再定义和父类同名的成员变量.

子类可以再定义和父类同名的方法.称为重写方法. 子类的对象调用方法时,系统会先在子类的方法定义中寻找方法,如果没有,再到父类中寻找.如果没有,再到父类的父类中寻找。

类方法和对象方法一样的寻找方式, 也可以重写.

@interface Person : NSObject
{
int _age;
}
- (void) run;
@end
@implementation Person
@end

@interface Student : Person
{
int _no;
// int _age;   不可以和父类的成员变量重名
}
- (void) run;    // 可以覆盖重写父类的方法
@end
@implementation Student
@end


#类在内存中的存储 #

每个对象在内存中都有一个isa指针,指向类的空间 (里面包含类的方法的声明). 其实每个类里面也有一个指针,叫做superclass,指向它的父类
(包含父类的方法声明)

(其实是NSObject里有一个成员变量是 Class类型的 isa.所以每个类里面才有这个isa指针(使用typedef把星号隐藏了,是个指针,指向Class类).可以打开NSObject的声明看一下)
(那superclass指针应该也是在NSObject里. )

在程序运行前, 先把所有的类加载进内存.

每个类中都有自己的方法列表 (里面包含类的方法的声明).如果一个子类中没有声明/实现任何方法,那它的方法列表就是空的.
(和属性不同,如果一个子类没有声明任何属性,它的对象的空间里还是有父类声明的属性)

每个类中都有一个指针, 指向自己的父类 (OC单继承). 当调用方法时,如果在自己的方法列表中没有对应的方法,就通过指针找到父类,在父类的方法列表中寻找对应的方法.

创建对象时,给每个对象开辟一块存储空间.这块空间中存放对象自己的成员变量
(包括父类声明的成员变量),以及一个isa指针 (NSObject声明的成员变量),
isa指针指向它的类所在的空间.

当对象调用方法时, 通过isa指针找到类的方法列表,去调用方法.如果自己的类的方法列表中没有对应的方法,就通过类的superclass指针找到父类的方法列表,在那里再找对应的方法. 比如
[Student new]方法的调用过程.



#
继承与组合#

对象中有sia指向了类,类中有superclass指向了父类

继承的使用场合:a.当两个类拥有相同属性和方法时,可以把相同的部分抽取到父类中.

b.当A类完全拥有B类中的部分属性和方法时可以考虑B继承A。

比如:

A类含有属性 _age, _no

B类含有属性 _age, _no, _weight

可以考虑让B类继承A类.

但不是什么时候都可以这样用.

比如:狗有两个属性, 年龄和体重

人类有三个属性, 年龄,体重,身高.

但是不能让狗继承人. 语法上可以,但是逻辑上不合理.

这种情况不适用继承, 应该使用组合.让人拥有狗类型的成员变量,这样狗就是组成人的一部分.

@interface Person : NSObject {

Dog *_dog; // Dog对象

int _height;

}

继承关系: B是A B : A 学生是人 Student : Person

组合关系: B有A B {属性A } Student{Score *_score;........}

根据二者的逻辑关系选择.

继承的好处:

建立了两个类之间的关系;

抽取重复代码;

子类可以拥有父类所有的方法和成员变量

注意:
父类声明必须在子类前边,实现写在后边无所谓
重写:子类实现父类中某个方法,覆盖父类以前的方法
子类不能拥有父类同样的成员变量
调用某个对象方法时候,优先在当前找,找不到了再去父类找
#self关键字的使用 #

self 是个指针,指向调用它的变量 (可能是类,也可能是对象).

self关键字不以@开头.

使用self访问当前对象的成员变量

@interface Person : NSObject
{
int _age;
}
- (void) setAge:(int)age;
- (int) age;
- (void) printAge;
@end
//在对象的方法里可以使用self关键字, self就指向调用这个方法的对象.
- (void) setAge:(int)age {
self->_age = age;
}
//和 _age = age; 是等价的


#import <Foundation/Foundation.h>

@interface Dog : NSObject

- (void)bark;
- (void)run;
@end

@implementation Dog

- (void)bark
{
NSLog(@"jiaoleyixia");
}

- (void)run
{
[self bark]; // self在这里代表d
NSLog(@"paoleyixia");
}

@end

int main()
{
Dog *d = [Dog new];

[d run];

return 0;
}


使用场合:

如果方法里有一个局部变量和对象的成员变量是同名的,
那么使用_age调用的就是局部变量,但是使用self->_age就可以忽略局部变量,调用成员变量.

使用self调用方法

a.self用在方法当中代表方法调用者, 谁调用这个方法,
self就代表谁. 可以使用self来调用其它类方法或者对象方法.

b.对象方法中的self代表调用该方法的对象,所以只能调用其它对象方法,不能调用类方法.

c.类方法中的self只能调用其它类方法,不能调用对象方法,因为self代表调用该方法的类.

d.如果在方法内部调用这个方法本身,会造成递归,可能会导致无限循环.

e.self不能去调用函数,函数只能通过函数名(参数)方式调用,不可以通过self调用.

如:void haha() { ... }

这是函数, 就算是混在方法实现里也是函数,独立于类和对象存在.要通过函数调用的方式去调用.不要和方法混淆了.

总结

self的用途:用在方法中,谁调用这个方法, self就代表谁.

在对象方法中通过 self->_age 访问该对象的成员变量.

在对象方法中通过 [self test]调用对象方法.

在类方法中通过 [self test]调用类方法.

#super关键字的使用#

使用super关键字调用父类方法:

作用

a.直接调用父类的某一个方法

b.如果super处在对象方法中便调用父类的对象方法,.如果super处在类方法中便调用父类的类方法。

使用场合: 子类重写父类的方法时想保留父类原有的行为.

#多态#

前提: 没有继承就没有多态
含义: 子类的对象既可以用子类的指针指向,也可以用父类的指针指向.即对象可以具备多种形态,即父类指针指向子类对象。

假如有Dog,Animal俩个类并且Dog继承Animal且拥有-
(void) eat;方法

Animal *a = [Dog new]; //多态,即父类指针指向子类对象

[a eat]; //使用 [a eat]
调用方法时, 依然是a所指向的对象来调用相应的方法,所以即使a是Animal类型的指针,但是它指向的是Dog类型的对象,所以调用的是Dog对象的方法,而不是Animal对象的方法.

// 僵尸类 - 父类
@interface Zombie : NSObject
- (void) walk;
+ (void) haha;
@end
@implementation Zombie
- (void) walk {
NSLog(@"Walk two steps.");    // 走两步
}
+ (void) haha {
NSLog(@"HAHAHA!");
}
@end

// 跳跃僵尸类 - 子类
@interface JumpZombie : Zombie
- (void) walk;     // 重写走路动作为走之前跳两下.
@end
@implementation JumpZombie
- (void) walk {
NSLog(@"Jump twice.");       // 先跳两下
[super walk];                          // 再调用父类的方法走两步
// [super haha];  会出错. 父类没有 - haha方法,有的是+haha, 是两个不同的方法
}
@end


# 多态的好处 #

好处: 如果函数/方法的参数中使用父类类型,这样便可以用一个父类指针变量指向多个子类对象。可以传入父类对象,也可以传入子类对象。即用父类接受参数,节省代码。

比如,
我们有一个Cat类,有eat方法.还有一个Dog类,也有eat方法.

@interface Cat : Animal
- (void) eat;
@end

@implementation Cat
- (void) eat {
NSLog(@"Cat is eating");
}
@end

我们想写函数来喂动物, 喂Dog和Cat由于参数类型不同,需要对每种类型都写一个函数:
void feed(Dog *d) {
[d eat];
}
void feed2(Cat *c ) {    // 不允许同名函数
[c eat];
}
int main() {
Dog *d = [Dog new];
feed (d);      // 喂狗
Cat *c = [Cat new];
feed2 (c);    // 喂猫
return 0;
}
这两个函数体是很相似的, 而且如果有更多的动物,还要写很多相似的函数,很麻烦,还要使用不同的名字.

可以使用多态来简化 - 写一个Animal类,作为Dog和Cat的父类,这样在函数中就可以使用Animal *指针,来指代Dog或Cat类型的参数:
void feed(Animal *a) {     // a既可以是Dog,也可以是Cat,也可以是其它Animal
[a eat];         // 调用对象的eat方法.
}
int main() {
Dog *d = [Dog new];
feed (d);        // 调用的是Dog的eat方法
Cat *c = [Cat new];
feed (c);        // 调用的是Cat的eat方法
Animal *a = [Animal new];
feed (a);
}


这样只需要写一个函数, 很方便,而且调用的还都是每个对象自身的方法.

#多态的局限性#

动态绑定:调用方法时会监测对象的真实类型的方法。
若 Animal中有 - (void) eat;
Dog中有 - (void) eat;
- (void) run;
即当两者有相同的方法时
Animal *a = [Dog new];
[a eat];这样是可以的,调用Dog中的
eat方法。

不建议用父类指针调用子类的特有方法
如 [a run];
如果用父类指针调用子类在父类中没有的方法,虽然调用的是子类对象的方法,但是编译器会报警告,因为它只能看到用父类的指针调用一个父类没有的方法.

可以正确运行,但是编译器会警告.不建议这么写 (不建议写编译器会出警告的代码),代码不规范.
如果必须要这么写,规范方法是进行强制类型转换.
Dog *d = (Dog *)a;
[d run];

#总结#
前提:有继承才有多态
多态:用父类指针指向子类对象
好处:可以用父类指针指代各种子类对象直接调用方法
局限:不建议用父类指针调用子类的特有方法,除非进行强制转换.

NSString类的一些知识

#import <Foundation/Foundation.h>

int main()
{
NSString *p = @"itcast"; // NSString字符串类,定义一个新的字符串

NSLog(@"%@",p);

int a = 10;
int b = 20;
NSString *_name = @"jack";

NSString *newstr = [NSString stringWithFormat:@"there are %d students in the %d room,his name is%@",a,b,_name]; // 定义一个对象,newstr,然后把a,b,name都加上去,动态实现

int s = [newstr length]; // 计算字符串的长度的方法,这是一个对象方法

NSLog(@"%@,%d",newstr,s);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: