018-继承-OC笔记

/ 0

学习目标

1.【掌握】Xcode开发文档

2.【掌握】static关键字

3.【掌握】self关键字

4.【掌握】继承

5.【掌握】NSObject

6.【掌握】访问修饰符

7.【掌握】私有实例变量和私有方法

8.【掌握】多态

9.【掌握】description

一、Xcode开发文档

苹果提供了很多的框架,每个框架中有很多类,每个类有很多方法,每个方法也有各种不同的用法。我们怎么查看这些框架里的这些类里的这些方法呢?苹果为我们提供了一个全面、丰富、牛逼的开发文档,只要我们装上就能随时查看了。

安装和查看文档

在线安装:Xcode -> Preferences -> Downloads -> Documentation,选中需要下载的直接下载,国内下载非常慢。(不推荐)

离线安装:网上下载离线包,复制到文档路径:

/Applications/Xcode.app/Contents/Developer/Documentation/DocSets里。(推荐)

查看文档:Window -> Documentation and API Reference 里可查看。

二、static关键字

static关键字在OC中也能使用,但是不能修饰属性也不能修饰方法。但可以修饰方法内部的局部变量,被static修饰的局部变量是静态变量,存储在常量区,当方法执行完毕之后,局部变量不会被回收。下次再声明的时候不会再重新声明一次,二是直接使用。

使用场景:当我们需要每次创建学生时就为学生自动赋值学号

Student.h文件

#import <Foundation/Foundation.h>

@interface Student : NSObject
{
    //学号
    int _studyId;
}
//_studyId的get方法声明
- (int)studyId;

//实例化一个对象的类方法声明
+ (Student *)student;
@end

Student.m文件

#import "Student.h"

@implementation Student
//_studyId的get方法实现
- (int)studyId{
    return _studyId;
}

//实例化一个对象的类方法实现
+ (Student *)student{
    //将局部变量用static关键字修饰为静态变量
    static int studyIdTemp = 1;
    
    //实例化一个对象student
    Student *student = [Student new];
    
    //为student对象的_studyId实例变量赋值局部变量studyIdTemp
    student->_studyId = studyIdTemp;
    
    //每次调用这个类方法实例化一个对象,studyIdTemp自增1
    studyIdTemp++;
    
    //返回student对象给调用者
    return student;
}
@end

main.m文件

#import <Foundation/Foundation.h>
#import "Student.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //调用Student的类方法实例化3个对象,会在类方法实现内部自动为学号赋值
        Student *student1 = [Student student];
        Student *student2 = [Student student];
        Student *student3 = [Student student];
        NSLog(@"他们的学号分别为student1 = %i student2 = %i student3 = %i",[student1 studyId],[student2 studyId],[student3 studyId]);
        //输出 他们的学号分别为student1 = 1 student2 = 2 student3 = 3
    }
    return 0;
}

从上面例子不难得出,当我们希望一个方法内部的局部变量能被多次调用,在方法执行完毕后不被销毁,这个时候我们就可以使用static来修饰我们的局部变量。

三、self关键字

self与对象方法

在方法的实现内,是可以声明一个和实例变量同名的局部变量,此时局部变量会屏蔽掉同名的实例变量的作用域(在局部变量的作用域范围内屏蔽掉实例变量的作用域)。但是我们可以使用self来访问被屏蔽作用域的实例变量,这个时候self指针指向当前对象(谁调用就指向谁)。(PS:我也不知道这样形容严谨不,就是这个意思哈,你懂就行)

如果方法中有一个局部变量和实例变量同名,要访问实例变量必须使用self,不过我们在开发中一定不要在方法实现内声明和实例变量同名的变量。

应用案例:

Person.h文件

#import <Foundation/Foundation.h>
@interface Person : NSObject
{
    NSString *_name;
    int _age;
}
//_name的set和get方法声明
- (void)setName:(NSString *)name;
- (NSString *)name;
//_age的set和get方法声明
- (void)setAge:(int)age;
- (int)age;
@end

Person.m文件

#import "Person.h"
@implementation Person
//_name的set和get方法实现
- (void)setName:(NSString *)name{
    NSString *_name = @"同名变量";
    self->_name = name;//这里就是将传入的值赋值给当前对象的实例变量_name
}
- (NSString *)name{
    return _name;
}
//_age的set和get方法实现
- (void)setAge:(int)age{
    int _age = 18;
    _age = age;//这里不使用self则是赋值给局部变量_age
}
- (int)age{
    return _age;
}
@end

main.m文件

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //实例化一个p对象,并调用set方法赋值
        Person *p = [Person new];
        [p setName:@"周剑峰"];
        [p setAge:20];
        
        NSLog(@"name = %@ age = %i",[p name],[p age]);
        //打印出 name = 周剑峰 age = 18
        //而不是 name = 周剑峰 age = 20
    }
    return 0;
}

self与类方法

self也可以在类方法中使用,这个时候self指针指向当前类。所以我们就能通过self来调用其他类方法,并且强烈推荐使用这种方式来调用其他类方法,而不是通过类名来调用。

应用案例:

Person.h文件

#import <Foundation/Foundation.h>
@interface Person : NSObject
//声明两个类方法
+ (void)sayHi;
+ (void)sayHello;
@end

Person.m文件

#import "Person.h"
@implementation Person
//在类方法实现里调用另一个类方法
+ (void)sayHi{
    NSLog(@"类方法sayHi");
    [self sayHello];//建议使用self调用
    [Person sayHello];//不建议使用类名调用
}
+ (void)sayHello{
    NSLog(@"类方法sayHello");
}
@end

main.m文件

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [Person sayHi];
        /*
         输出
         类方法sayHi
         类方法sayHello
         类方法sayHello
         */
    }
    return 0;
}

四、继承

QQ20150718-2

当多个类具有相同成员的时候,那么这个时候就可以把相同的成员抽取出来定义到一个类中,让其他类继承这个类。当建立了继承关系,子类(派生类)就拥有父类(基类)所有成员,并且子类可以重写父类方法也能新增子类特有的方法和实例变量。

语法:@interface 子类类名 : 父类类名

Person.h文件

#import <Foundation/Foundation.h>

//性别枚举
typedef enum {
    GenderMale,
    GenderFemale
}Gender;

@interface Person : NSObject
{
    NSString *_name;
    Gender _gender;
    int _age;
}

//_name的set、get方法声明
- (void)setName:(NSString *)name;
- (NSString *)name;

//_gender的set、get方法声明
- (void)setGender:(Gender)gender;
- (Gender)gender;

//_age的set、get方法声明
- (void)setAge:(int)age;
- (int)age;

//父类吃食物方法声明
- (void)eatWithFoodName:(NSString *)foodName;
@end

Person.m文件

#import "Person.h"

@implementation Person

//_name的set、get方法实现
- (void)setName:(NSString *)name{
    _name = name;
}
- (NSString *)name{
    return _name;
}

//_gender的set、get方法实现
- (void)setGender:(Gender)gender{
    _gender = gender;
}
- (Gender)gender{
    return _gender;
}

//_age的set、get方法实现
- (void)setAge:(int)age{
    _age = age;
}
- (int)age{
    return _age;
}

//父类吃食物方法实现
- (void)eatWithFoodName:(NSString *)foodName{
    NSLog(@"%@吃%@",self->_name,foodName);
}
@end

Student.h文件

//导入Person类的头文件
#import "Person.h"

//Student类继承Person类
@interface Student : Person
{
    int _studyId;
}

//Student类特有实例变量的set、get方法声明
- (void)setStudyId:(int)studyId;
- (int)studyId;

//Student类特有学习方法
- (void)study;
@end

Student.m文件

#import "Student.h"

@implementation Student

//Student类特有实例变量的set、get方法实现
- (void)setStudyId:(int)studyId{
    _studyId = studyId;
}
- (int)studyId{
    return _studyId;
}

//Student类特有学习方法
- (void)study{
    NSLog(@"学生类特有的学习方法");
}
@end

main.m文件

#import <Foundation/Foundation.h>
#import "Student.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Student *stu = [Student new];
        
        //继承父类的实例变量
        [stu setName:@"周剑峰"];
        [stu setGender:GenderFemale];
        [stu setAge:20];
        
        //学生类的特有实例变量
        [stu setStudyId:2];
        
        //输出学生的信息
        NSLog(@"name = %@ gender = %d age = %i studyId = %i",[stu name],[stu gender],[stu age],[stu studyId]);
        
        //继承父类的方法
        [stu eatWithFoodName:@"米饭"];
        
        //子类特有的方法
        [stu study];
        
    }
    return 0;
}

注意:

1.构成继承关系的类,他们直接应该有所属关系。比如学生是一个人(学生类继承人类),狗是一个动物(狗是一个动物)。

2.从父类继承的方法,不适用于子类时,可以直接声明父类的同名方法,并定义(这就是重写方法)。

3.子类重写方法之后,在调用方法时,父类的对象调用父类的方法,子类的对象调用子类的方法,不会引起冲突。

4.子类重写方法之后,子类可以通过super关键字调用父类的类方法(这里的super也相当于一个指向父类的指针)。

5.只要某个成员不是所有子类都拥有的,那么这个成员就不应该定义在父类中。

6.一个类只能有一个父类,objective-c不支持多继承。

7.子类和父类中不能有同名的属性。

8.super关键字用在子类对象方法中,可以调用当前子类继承的父类的对象方法。

9.super关键字用在子类的类方法中,可以调用父类的类方法。

继承的特点

1.单根性:一个类只能有一个父类,objective-c不支持多继承。

2.传递性:当A类继承B类,B类又继承自C类。B类继承了C类所有成员,A类又继承了B类所有成员(包括B类继承的C类所有成员)。

五、NSObject

要实例化一个对象,必须要调用这个类的类方法new,而这个方法定义在NSObject之中。所以,为了保证我们的类拥有创建对象的能力,那么我们的类必须直接或间接的从NSObject类继承。

NSObject类是所有类的基类,在这个类中定义了一个实例变量isa指针,指向代码区中类的地址。

六、访问修饰符

访问修饰符是用来修饰类的实例变量的,被不同访问修饰符修饰的实例变量的访问权限是不同的。所以,访问修饰符的作用就是用来限制实例变量的访问权限的。

@public 共有的

被@public关键字修饰的实例变量可以通过对象名在任意的地方访问。

@protected 受保护的

被@protected关键字修饰的实例变量只能在本类和他的子类访问。如果实例变量没有被访问修饰符修饰,默认修饰符就是@protected。

@private 私有的

被@private关键字修饰的实例变量只能在本类的内部访问,子类能继承私有成员,但在子类方法实现中无法访问继承的私有成员。

@package 包权限

被@package关键字修饰的实例变量只能在当前框架中访问。

七、私有实例变量和私有方法

私有实例变量

将实例变量定义在@implementation中,这些实例变量就是私有实例变量,这些私有实例变量只能在本类中访问,不能在其他地方访问,并且Xcode都不提示类中存在的私有实例变量。注意:这种情况可以在子类中定义和父类私有实例变量同名的实例变量。

@implementation Person
{
    NSString *_name;
    //定义在这里的实例变量只能在本类中访问
}
@end

私有方法

没有方法的声明,只有方法的实现。这样的方法就只能在类的内部调用。

八、多态

多态就是父类指针指向子类对象

1.当方法的参数是一个父类类型的时候,那么传递的实参可以是父类对象也可以是子类对象。

2.当一个父类指针指向一个子类对象的时候,只能通过父类指针访问子类对象中的继承成员,子类独有的不能访问。

多态的好处

多态的主要好处就是简化了编程接口。它容许在类和类之间重用一些习惯性的命名,而不用为每一个新加的函数命名一个新名字。这样,编程接口就是一些抽象的行为的集合,从而和实现接口的类的区分开来。

多态也使得代码可以分散在不同的对象中而不用试图在一个函数中考虑到所有可能的对象。这样 使得代码扩展性和复用性更好一些。当一个新的情景出现时,无须对现有的代码进行改动,而只需要增加一个新的类和新的同名方法。

九、description

%@是专门用来输出对象的,默认情况下,我们将对象以%@的格式输出,输出的是

<对象所属的类名:对象的地址>

NSLog(@"%@",对象名);输出一个对象的时候,会先调用传入对象的description方法,这个方法的返回值是字符串(NSString *),然后将这个字符串输出。我们可以重写description方法,输出我们需要输出的任意内容。

- (NSString *)description{
    NSString *str = @"<<重写了description方法>>";
    return str;
}

这个时候我们在使用NSLog(@"%@",对象名);输出的是

<<重写了description方法>>