个人技术分享

前言

本周刚开始学习OC,本篇博客对自己第一周的学习做一个总结,梳理一下《疯狂IOS讲义》第五章的内容。

一、类和对象、方法、成员变量

1. 定义类

面向对象的程序设计过程中有两个重要概念:类(class)和对象(obiect,也称为实例,instance),其中类是某一批对象的抽象,可以把类理解成某种概念;对象才是一个具体存在的实体,从这个意义上看,日常所说的人其实都是人的实例,而不是人类。类和对象是面向对象的核心。 Obiective-C提供了创建类和创建对象的语法支持。Objective-C中定义类需要分为两个步骤。
接口部分:定义该类包含的成员变量和方法。
实现部分:为该类的方法提供实现。

类接口部分的语法(通常使用.h文件命名)

@interface Person/*类名*/:NSObject//父类
{
   int _age;//成员变量声明
}
-(void)run;//方法声明
-(void)jump;
-(void)eat:(int)num;
-(void)eatApple:(int) num1 andPear:(int)num2; 
@end

@interface 用于声明定义类的接口部分
@end 用于表明定义结束
成员变量 描述该类的对象的状态数据
方法 用于描述该类的行为。

定义成员变量的语法:
类型:可以是OC语言允许的任何数据类型。
成员变量名: 遵循驼峰命名规则。

2.类实现部分

语法格式(通常使用.m文件命名)

@implementation Person
{
  int _age;//成员变量声明
}
-(void)run//无参无返回值
{
   NSLog(@"人在跑");//方法体
}

-(void)eat:(int)num//一个参数
{
   NSLog(@"人吃%d个苹果,num");
}

-(void)eatApple:(int) num1 andPear:(int)num2//多个参数
{
   NSLog(@"人吃%d个苹果和%d个梨,num1,num2");
}
@end

类实现部分的说明

  • 类实现部分的类名必须和类接口部分的类名一致
  • 类实现部分也可以声明自己的成员变量,但仅可以在当前类内访问
  • 类实现部分必须为类声明部分的每个方法提供方法定义

3.方法

方法是类或对象的行为特征的抽象,也是类或对象最重要的组成部分。从功能上看,方法完全类似于传统结构化程序设计里的函数。值得指出的是,Obiective-C的方法不能独立存在,所有的方法都必须定义在类里。方法在逻辑上要么属于类,要么属于对象。+ 号表示为类方法;- 号表示是实例方法

方法的所属性

  • 方法不能独立定义,只能在类体里定义。
  • 从逻辑意义上看,方法要么属于该类本身,要么属于该类的一个对象。
  • 永远不能独立执行方法,执行方法必须使用类和对象作为调用者。

上面的代码块中,已经举出无参数,一个参数,多个参数方法的例子,下面只对形参个数可变的方法进行说明。

参数可变的方法

//定义参数个数可变的方法
@interface Person:NSObject
{
   int _age;
}
- (void)drinkCoke:(int) num, ...;
@end

如果要获取个数可变的形参,需要使用如下关键字

在这里插入图片描述

本质上,这个可变参数也是一个类似数组的结构

4.对象的产生和使用

定义类以后,可以从以下三个方面使用:

  • 定义变量
  • 创建对象
  • 调用类方法

定义变量的语法为
类名* 变量名
创建对象的语法为
[[类名 alloc]初始化方法];

alloc是OC的关键字,该关键字负责为该类分配内存空间、创建对象。
由于所有的对象都继承至NSObject类,故而所有的类都有一个默认的初始化方法:init。

OC调用方法的语法格式为:
[调用者 方法名:参数 形参标签:参数值 ...];

举例

Preson* p=[[Preson alloc]init];
[p run];//无参数
[p eat:7];//一个参数
[p eatApple: 4 andPear: 7];//多个参数

4.1 self关键字

Objective-C提供了一个self关键字,self 关键字总是指向该方法的调用者(对象或类),当self出现在实例方法中时,self代表调用该方法的对象;当self出现在类方法中时,self代表调用该方法的类。self关键字最大的作用是让类中的一个方法访问该类的另一个方法或成员变量。
举例:

@implementation Person
{
  int _age;
}
-(void)run
{
   NSLog(@"人在跑");
}
//实现一个jump方法,jump方法需要借助run方法
-(void)jump
{
   [self run];
   NSLog(@"人在跳");
}
@end

在局部变量和成员变量重名的情况下,局部变量会隐藏成员变量。为了在方法中强行引用成员变量,也可以使用self关键字进行区分。

当self作为对象或类本身的默认引用使用时,程序可以像访问普通指针变量一样访问这个self引用,甚至可以把self当成普通方法的返回值。

4.2 id类型

Objective-C提供了一个id类型,这个id类型可以代表所有对象的类型。也就是说,任意类的对象都可赋值给id 类型的变量。
当通过id 类型的变量来调用方法时,Objective-C将会执行动态绑定。所谓动态绑定,是指 Objective-C将会跟踪对象所属的类,它会在运行时判断该对象所属的类,并在运行时确定需要动态调用的方法,而不是在编译时确定要调用的方法。

5. 成员变量

在 Objective-C语言中,根据定义变量位置的不同,可以将变量分成三大类:成员变量、局部变量和全局变量。
全局变量的知识在前面已有详细介绍。由于方法是由函数演化而来的,因此,无论是方法中定义的变量,还是函数中定义的变量,它们都是局部变量。

实例变量语法
实例->实例变量

在这里插入图片描述

5.1 模拟类变量

Objective-C也提供了 static 关键字,但这个 static 关键字不能用于修饰成员变量,它只能修饰局部变量、全局变量和函数,static 修饰局部变量表示将该局部变量存储到静态存储区;static 修饰全局变量用于限制该全局变量只能在当前源文件中访问;static 修饰函数用于限制该函数只能在当前源文件中被调用。

5.2 单例模式

如果一个类始终只能创建一个实例,则这个类被称为单例类。
单例类可通过 static 全局变量来实现,此处考虑定义一个static 全局变量,该变量用于保存已创建的 Singleton 对象–每次程序需要获取该实例时,都要先判断该static 全局变量是否为nil,如果该全局变量为nil,则初始化一个实例并赋值给 static 全局变量:如果该全局变量不为nil,那么程序直接返回该全局变量指向的实例即可。
单例模式的创建
接口部分

@interface FKSingletion : NSObject {
  ;
}
+ (id) instance; 
@end

实现部分

static id instance = nil;
@implementation FKSingletion
+ (id) instance {
    if(!instance) {
		instance = [[super alloc] init];
    }
  return instance;
}
@end

二、隐藏和封装

封装(Encapsulation)是面向对象的三大特征之一(另外两大特征是继承和多态),它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
封装是面向对象编程语言对客观世界的模拟,在客观世界中对象的成员变量(用于描述对象的状态数据)都被隐藏在对象内部,外界无法直接操作和修改。

对一个类或对象实现良好的封装,可以实现以下目的。

  • 隐藏类的实现细节。
  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对成员变量的不合理访问。
  • 可进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。

为了实现良好的封装,需要从以下两个方面考虑。

  • 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问。
  • 把方法暴露出来,让方法来控制对这些成员变量进行安全的访问和操作。

封装实际上有两方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来。这两个方面都需要通过使用 Objective-C 提供的访问控制符来实现。

1. 使用访问控制符

在这里插入图片描述

  • @private(当前类访问权限):如果类的成员变量使用@private访问控制符来限制,则这个成员变量只能在当前类的内部访问。很显然,这个访问控制符用于彻底隐藏成员变量。在类的实现部分定义的成员变量相当于默认使用这种访问权限。
  • @package(相同映像访问权限):如果类的成员变量使用@package访问控制符来限制则这个成员变量可以在当前类以及当前类的同一个映像的任意地方访问。很显然,这个访问控制符用于部分隐藏成员变量。
  • @protected(子类访问权限):如果类的成员变量使用@package访问控制符来限制,则这个成员变量可以在当前类、当前类的子类的任意地方访问。很显然,这个访问控制符用于部分暴露成员变量。在类的接口部分定义的成员变量默认使用这种访问权限。
  • @public(公共访问权限):这是一个最宽松的访问控制级别,如果类的成员变量使用@public 限制,那么这个成员变量可以在任意地方访问,不管是否处于同一映像中,也不管是否具有父子继承关系。

在这里插入图片描述

2. setter、getter方法

setter方法命名规则
1、以-号开头
2、不需要返回值
3、方法名必须以set开头,后面是对象的成员变量去掉下划线后首字母大写。
4、只有一个参数,参数的类型为对应成员变量的类型,参数的名是对象的成员变量去掉下划线后的部分
getter方法的命名规则
1、以-号开头
2、返回值类型为对应成员变量的类型2、
3、方法名为对应成员变量去掉下划线后的部分
4、不需要参数

举例
在这里插入图片描述

3. 合成存取方法

  1. 在OC还可以自己生成一个setter和getter方法,我们则需要使用**@property**指令来定义属性。使用这个指令的时候我们无须将他直接放在@interface和@end之间进行一个定义,这个指示符放在属性定义的前面。
@interface Person:NSObject
@property int age;
@end
  1. 在类的实现部分使用一个**@synthesize**指令来声明该属性
@implementation Person
@synthesize age=_age;
@end

特殊指示符
assign:该指示符指定对属性只是进行简单的赋值,不更改对所赋的值的引用计数。
atomic(nonatomic):指定合成的存取方法是否为原子操作。
copy:如果使用copy指示符,那么当调用setter 方法对成员变量赋值时,会将被赋值的对象复制一个副本,再将该副本赋值给成员变量。copy指示符会将原成员变量所引用对象的引用计数减1。

4. 使用点语法访问属性

点语法是一种非常简单的用法。点语法只是一种简化写法,其本质依然是调用getter和setter 方法。当程序调用点语法获取指定对象的属性值时,本质上就是获取该对象的getter 方法的返回值。因此,只要该对象有getter方法(无论该对象是否存在对应的成员变量),程序就可以通过点语法来获取属性值:当程序调用点语法设置对象的属性值时,本质上就是调用该对象的 setter 方法进行设置。因此,只要该对象有setter 方法(无论该对象是否存在对应的成员变量),程序就可以通过点语法来设置对象的属性值。
在这里插入图片描述在这里插入图片描述

三、 对象初始化

使用alloc来初始化的时候,系统回味该对象的所有实例变量分配内存空间,将每个实例变量都重置为0,所有BOOL型变量都重置为NO,所有的指针类型变量都重置为nil。

- (id) init {
    if (self = [super init]) { //这一步的意思是我们先对self进行一个初始化
        self.name = @"张三";
        self.age = 18;
    }
    return self;
}

便利的初始化方法

由于上面的方法不可以对参数执行动态的初始化,我们可以使用initXxx根据参数执行自定义初始化。
举例
在这里插入图片描述
在这里插入图片描述

四、类的继承

继承是面对对象的三大特征之一,也是实现软件服用的重要手段。OC的继承具有单继承的特点,每个子类只有一个直接父类

Objective-C的继承通过“:父类”语法来实现,实现继承的类被称为子类,被继承的类被称为父类,也称其为基类、超类。父类和子类的关系是一种“一般和特殊”的关系。例如,水果和苹果的关系,苹果继承了水果,苹果是水果的子类,苹果是一种特殊的水果。

@interface Student:Person
{
   //成员变量定义
}
@end

这里表明Student继承的是Person类。继承关系的本质是一种“由一般到特殊”的关系,子类继承父类,子类是一种特殊的父类。从这个意义上看,使用继承来描述子类和父类的关系是错误的,可能更适合用扩展来描述。
举例

@interface Person : NSObject
-(void) run;
@end
@interface Student : Person 

@end


@implementation Person 
-(void) run;
{
     NSLog(@"人在跑");
}
@end
@implementation Student 
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Student* p = [[Student alloc] init]; 
       [p run];
    }
    return 0;
}

4.1 重写父类的方法

因为之前重写过init方法,同时我们所有的类都是NSObject的一个子类,从上面我们就发现了如果重写一个方法的话,一定要做到的要求是:方法签名关键字要完全相同,也就是方法名和方法签名中的形参标签都需要完全相同,否则就不算是方法重写。

4.2 super关键词

如果需要在子类方法中调用父类被覆盖的方法,则可使用super 关键字来调用父类被覆盖的方法。

super既可出现在类方法中,也可出现在实例方法中。在类方法中使用super 调用父类的方法时,被调用的父类方法只能是类方法;在实例方法中使用super 调用父类的方法时,被调用的父类方法只能是实例方法。
当子类继承父类时,子类可以获得父类中定义的成员变量,因此,子类接口部分不允许定义与父类接口部分重名的成员变量。