iOS开发-面试总结(十一)
OC基本语法总结(面试)
1:C和OC对比
OC中主要开发在什么平台上的应用程序?
- 答:可以使用OC开发Mac OS X平台和iOS平台的应用程序
OC中新增关键字大部分是以什么开头?
- 答:OC中新增关键字大部分是以@开头
OC中新增加了那些数据类型?
答:
- Block类型
- 指针类型(Class, id类型)
- 空类型
- 特殊类型(SEL, nil)
面向对象特性是什么?
- 答:继承性,封装性,多态性
import和#include有什么区别?
- 答:import 的功能和 include一样, 是将右边的文件拷贝到当前import的位置.为了降低程序员的负担, 防止重复导入, 避免程序员去书写 头文件卫士, 那么OC给出来一个新的预处理指令import
- import优点: 会自动防止重复拷贝
printf和NSLog有什么区别?
- 答: NSLog会自动换行
- NSLog在输出内容时会附加一些系统信息
- NSLog和printf接收的参数不一样
Foundation有什么作用?
- 答:Foundation.h我们称之为主头文件, 主头文件中又拷贝了该工具箱中所有工具的头文件, 我们只需要导入主头文件就可以使用该工具箱中所有的工具, 避免了每次使用都要导入一个对应的头文件
2:面向对象基
什么是面向对象?
- 答:对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。
面向对象和面向过程的区别?
答:
- 1>面向对象是相对面向过程而言
- 2>面向对象和面向过程都是一种思想
面向过程
- 强调的是功能行为
- 关注的是解决问题需要哪些步骤
面向对象
- 将功能封装进对象,强调具备了功能的对象
- 关注的是解决问题需要哪些对象
3:类与对象
什么是对象?
- 答:对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。
什么是类?
- 答:具有相同特性(数据元素)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。
类由几部分组成?
- 答: 类(Class) 一个类由3个部分构成
- 类的名称:类名
- 类的属性:一组包含数据的属性
- 类的方法:允许对属性中包含的数据进行操作的方法
怎么来定义一个类?书写类的格式
- 答:定义类要分为两部分: 声明和实现
类的声明格式
- @interface 类名 : 父类名(NSObject)
- {
- 定义实例变量(成员变量,属性)
- }
方法的声明
- -(view)action;
类的实现格式
- @implementation 类名
- 方法的具体实现
- @end;
OC中属性写在类中哪个位置?
- 答:OC类声明中属性只能在写@interface和@end之间的{}中
如何通过类创建对象?书写创建对象格式
- 答: 创建对象格式:
- 类名 *指针名 = [类名 new];
实例化对象调用哪个方法?实例化对象做了哪3件事?
- 答:实例化对象调用类方法new
1.给对象分配存储空间
2.初始化类中的实力变量
3.返回对象内存地址
如何访问对象中的属性?
- 答:使用指针访问
- 格式: 指针名->_属性名;
4:OC方法
方法和函数的区别?
答:
- 1)OC中的行为和C语言中的函数一样, 都是用来保存一段特定功能的代码
- C语言中定义一个函数, 分为声明和实现, 声明写在.h中, 实现写在.c中
- OC中定义一个方法, 也分为声明和实现, 声明写在@interface中, 实现写在@implementation
- C语言的函数分为两种: 内部函数和外部函数
- OC中的方法也分为两种; 类方法和对象方法
怎么声明和实现无参无返回值方法的?调用方法的格式?(书写格式)
- 答:
- 方法的声明格式:
- (返回值类型)方法名;
- 方法的实现格式:
- (返回值类型)方法名{
- }
- 调用方法格式:
- [对象名 方法名];
- 或者 [类名 方法名];
小括号在OC方法中有什么用法?
- 答:OC方法中的()有特殊的用途, OC方法中的()是用来扩住数据类型的
有参数方法格式是怎样?
- 带一个参数
- 方法声明
- ·- (返回值类型)方法名:(参数类型)参数名;
- 方法实现
- - (返回值类型)方法名:(参数类型)参数名{
- }
带多个参数
- 方法声明
- - (返回值类型)方法名1:(参数类型)参数名1 方法名2:(参数类型)参数名2;
- - (返回值类型)方法名1:(参数类型)参数名1 and方法名2:(参数类型)参数名
- - (返回值类型)方法名1:(参数类型)参数名1 :(参数类型)参数名2;
- 方法实现
- - (返回值类型)方法名1:(参数类型)参数名1 方法名2:(参数类型)参数名2{
- }
5:OC类方法
类方法和对象方法的区别?
答:
0). 对象方法以-开头
- 类方法以+开头
1). 对象方法必须用对象调用
- 类方法必须用类来调用
2).对象方法中可以直接访问属性(成员变量)
- 类方法中不可以直接访问属性(成员变量)
3). 类方法和对象方法可以进行相互调用(展示代码)
类方法的应用场景?
答:
- 类方法一般用于定义工具方法
- 字符串查找
- 文件操作
- 数据库操作
isa是什么数据类型?
- 答: 类的第0个属性并不是我们编写的_age, 而是一个叫做isa的属性
- isa是对象中的隐藏指针,指向创建这个对象的类,占8个字节
6:方法和函数的区别
函数和方法的区别?
答:
1).函数属于整个文件, 方法属于某一个类
- 方法如果离开类就不行
2).函数可以直接调用, 方法必须用对象或者类来调用
- 注意: 虽然函数属于整个文件, 但是如果把函数写在类的声明中会不识别
3).不能把函数当做方法来调用, 也不能把方法当做函数来调用
方法有哪些的注意点?
- 答:
- 方法可以没有声明只有实现
- 方法可以只有声明没有实现, 编译不会报错, 但是运行会报错
- 如果方法只有声明没有实现, 那么运行时会报:
- reason: ‘+[Person demo]: unrecognized selector sent to class 0x100001140’
- 发送了一个不能识别的消息, 在Person类中没有+开头的demo方法
- reason: ‘-[Person test]: unrecognized selector sent to instance 0x100400000’
常见的错误有哪些?
- 1.只有类的声明,没有类的实现
- 2.漏了@end
- 3.@interface和@implementation嵌套
- 4.成员变量没有写在括号里面
- 5.方法的声明写在了大括号里面
- 6.成员变量不能在{}中进行初始化、不能被直接拿出去访问
- 7.方法不能当做函数一样调用
- 8.OC方法只能声明在@interface和@end之间,只能实现在@implementation和@end之间。也就是说OC方法不能独立于类存在
- 9.C函数不属于类,跟类没有联系,C函数只归定义函数的文件所有
- 10.C函数不能访问OC对象的成员
- 11.低级错误:方法有声明,但是实现的时候写成了函数
- 12.OC可以没有@interface同样可以定义一个类
7:多文件开发
为什么要使用多文件?
答:
- 一个iOS项目可能会有多个人开发,如果多个人同时修改一个文件,那么就很可能会产生冲突,比如这个增加一个方法,那个人把这方法删掉了。另外就是当把多个人写功能合并起来的时候,也非常困难,写到一个文件中,无法顺畅的进行团队合作
OC中如何进行多文件开发?
答:
- 在工作中,通常把不同的类放到不同的文件中,每个类的声明和实现分开
- 声明写在.h头文件中,
- 实现写在相应的.m文件中去,
- 类名是什么,文件名就是什么
使用多文件开发有什么好处?
答:
- 显著提高团队协作的效率
- 提高程序的开发速度
- 提高程序的可维护性
- 提高代码的可读性
8:面向对象特性
:面向对象三大特性有哪些?
- 答:继承性,封装性,多态性
什么是封装?封装的好处?封装的规范?
答:
- 封装: 屏蔽内部实现的细节, 仅仅对外提供共有的方法/接口
- 好处: 保证数据的安全性,将变化隔离
- 规范: 一般情况下不会对外直接暴露成员变量, 都会提供一些共有的方法进行赋值
- 成员变量都需要封装起来
为什么要进行封装?
- 答:一个类把自己的成员变量暴露给外部的时候,那么该类就失去对该成员变量的管理权,别人可以任意的修改你的成员变量。
9:geeter-setter方法
什么是setter方法?sett方法的书写格式?
- 答:setter方法就是给成员变量赋值的方法
格式:
- (1) setter方法一定是对象方法
- (2) 一定没有返回值
- (3) 一定以set开头, 并且set后面跟上需要设置的成员变量的名称去掉下划线, 并且首字母大写
- (4) 一定有参数, 参数类型一定和需要设置的成员变量的类型一致, 并且参数名称就是成员变量的名称去掉下划线
getter方法就是获取成员变量值的方法
格式:
- (1) getter方法一定是对象方法
- (2)一定有返回值, 而且返回值一定和获取的成员变量的类型一致
- (3)方法名称就是获取的成员变量的名称去掉下划线
- (4) 一定没有参数
成员变量以下划线开头有什么好处?
- 答:
- 1.用于区分局部变量/全局变量/形参
- 2.方便程序编码, 提高编码效率
解释一下什么是只读属性?什么是只写属性?可读可写的属性?私有属性?
答:
- 一个属性可以只有getter方法, 没有setter方法, 这种属性我们称之为只读属性
- 一个属性也可以只有setter方法, 没有getter方法, 这种属性我们称之为只写属性
- 如果既有setter方法又有getter方法, 那么这种属性我们称之为可读可写的属性
- 一个属性也可以没有getter和setter, 这种属性我们称之为私有属性
10:点语法和self关键字
什么是点语法?点语法的本质?
- 答:如果给属性提供了getter和setter方法, 那么访问属性就又多了一种访问方式 , 点语法.
- 点语法的本质是调用了一个类的setter和getter方法
如何使用点语法?
答:
- 点语法是一个编译器的特性, 会在程序翻译成二进制的时候将.语法自动转换为setter和getter方法
- 如果点语法在=号的左边, 那么编译器会自动转换为setter方法
- 如果点语法在=号的右边, 或者没有等号, 那么编译器就会自动转换为getter方法
点语法注意事项?
- 答:点语法一般用于给成员变量赋值, 如果不是给成员变量赋值一般情况下不建议使用, 但是也可以使用
什么是成员变量?什么是对象方法?什么是类方法?
答:
- 成员变量:
- 成员变量是一个实例对象的具体状态特征,并且这些状态特征是可以改变的,如张三的年龄,身高,体重等
对象方法:
- 一个实例对象的行为,比如张三具有吃的行为,张三做出这样行为的时候,有可能会影响,自身的某些状态特征,比如张三吃可能会增加张三体重和身高。
类方法:
- 类方法是某个类的行为,可以直接通过类名调用;如果在类方法中需要使用某些数据,必须通过参数传入;类方法不能访问成员变量。
如何使用self?
- 如果self在对象方法中, 那么self就代表调用当前对象方法的那个对象
- 如果self在类方法中, 那么self就代表调用当前类方法的那个类
总结:
- 我们只用关注self在哪一个方法中 , 如果在类方法那么就代表当前类, 如果在对象方法那么就代表”当前调用该方法的对象”
self有哪些注意事项?
答:
- (1)self会自动区分类方法和对象方法, 如果在类方法中使用self调用对象方法, 那么会直接报错
- (2)不能在对象方法或者类方法中利用self调用当前self所在的方法
self的有哪些使用场景?
答:
- 可以用于在对象方法之间进行相互调用
- 可以用于在类方法之间进行相互调用
- 可以用于区分成员变量和局部变量同名的情况
11:继承基本概念
什么是继承?什么是父类?什么是子类?如何实现继承?
答:
- 1)子类获得父类的特性就是继承
- 2)被继承的这个类我们称之为父类/ 超类
- 3)继承了某个类的类我们称之为子类
- 4)在声明子类的时候,在子类名称后面通过:父类名称方式来实现继承
@interface 子类名称 : 父类名称
@end类
当B类继承A类, 那么B类就拥有A类所有的属性和方法(类方法/对象方法)
什么叫方法重写?重写后是以什么顺序调用方法的?
答:
- (1)如果子类中有和父类中同名的方法, 那么我们称之为方法重写
- 注意: 继承中的方法调用顺序, 如果自己有就调用自己的, 如果自己没有就调用父类的
- “方法的调用顺序, 先自己再父类, 如果父类中没有再爷爷类, 如果爷爷类再没有就找爷爷的爸爸类
- 如果一直找到NSObject类都没有找到, 那么就会报错
- reason: ‘-[Iphone signalWithNumber:]: unrecognized selector sent to instance 0x1003043c0’
- 注意:在继承中方法可以重写, 但是属性(成员变量)不能重写
3.方法重写的使用场景?
- 答:使用场景:当从父类继承的某个方法不适合子类,可以在子类中重写父类的这个方法。
4.继承的条件是什么?
答:
- 不要以为继承可以提高代码的复用性, 以后但凡发现多个类当中有重复代码就抽取一个父类
- 只要满足一定的条件我们才能使用继承
- 条件: XXXX 是 XXX / 某某某 is a 某某某
5.继承的优点是什么?
答:
- 提高代码的复用性
- 可以让类与类之间产生关系, 正是因为继承让类与类之间产生了关系所以才有了多态
6.继承的缺点是什么?
答:
- 耦合性太强(依赖性太强)
12:super关键字
什么是super?
- super是个编译器的指令符号,只是告诉编译器在执行的时候,去调谁的方法.
怎么使用super?
答:
- super在类方法中, 一定会调用父类的类方法
- super在对象方法中, 一定会调用父类的对象方法
- 可以利用super在任意方法中调用父类中的方法
super使用场景?
答:
- 子类重写父类的方法时想保留父类的一些行为
13:多态
什么是多态?程序中是怎么体现多态的?
- 答:
- 多态就是某一类事物的多种形态
- 在程序中如何表现:
- 父类指针指向子类对象
多态的条件是什么?
答:
- 1)有继承关系
- 2)子类重写父类方法
- 3)父类指针指向子类对象
多态的优点是什么?
- 答:提高了代码的扩展性,复用性
多态的注意点?
- 答:如果父类指针指向子类对象, 需要调用子类特有的方法, 必须先强制类型转换为子类才能调用
14:description方法
使用%@了打印一个对象,输出的是什么内容?%@的原理是什么?
- 答:%@是用来打印对象的, description方法默认返回对象的描述信息(默认实现是返回类名和对象的内存地址).
- 其实%@的本质是用于打印字符串.
- 只要利用%@打印某个对象, 系统内部默认就会调用父类的description方法
- 调用该方法, 该方法会返回一个字符串, 字符串的默认格式 <类的名称: 对象的地址>
重写description方法注意点?
- 答:如果在description方法中利用%@输出self会造成死循环
- 建议: 在description方法中尽量不要使用self来获取成员变量
- 因为如果你经常在description方法中使用self, 可能已不小心就写成了 %@, self
15:私有变量和私有方法
什么是私有变量?什么是私有方法?
答:
- 实例变量(成员变量)既可以在@interface中定义, 也可以在@implementation中定义
私有变量:
- 写在@implementation中的成员变量, 默认就是私有的成员变量, 并且和利用@private修饰的不太一样, 在@implementation中定义的成员变量在其它类中无法查看, 也无法访问
私有方法:
- 在@implementation中定义的私有变量只能在本类中访问
property和synthesize基本使用
@porperty是一个编译器指令
- 在Xocde4.4之前, 可以使用@porperty来代替getter/setter方法的声明
- 也就是说我们只需要写上@porperty就不用写getter/setter方法的声明
- 编译器只要看到@property,就知道我们要生成某一个属性的
getter/setter方法的声明
@propertyde格式?
- 答:@property 数据类型 变量名;
@synthesize是什么指令?作用是什么?
答:
- synthesize是一个编译器指令, 它可以简化我们getter/setter方法的实现
@synthesize age = _age; 在给age赋值时,编译器做了哪些事?
@synthesize age = _age;
- (1)在@synthesize后面的age,告诉编译器, 需要实现哪个@property生成的声明
- (2)告诉@synthesize, 需要将传入的值赋值给谁和返回谁的值给调用者
如果在@synthesize后面没有告诉系统将传入的值赋值给谁, 系统默认会赋值给和@synthesize后面写得名称相同的成员变量
@synthesize age;
property增强做了哪些事?
答:
- (1)从Xcode4.4以后,对@property进行了增强, 以后只要利用一个@property就可以同时生成setter/getter方法的声明和实现
- (2)如果没有告诉@property要将传入的参数赋值给谁, 默认@property会将传入的属性赋值给_开头的成员变量
@property的使用场景?
答:
- 如果不想对传入的数据进行过滤, 仅仅是提供方法给外界操作成员变量, 那么就可以使用@property,并且系统会自动给我们生成一个_开头的成员变量
使用property增强后,什么时候要重写getter/setter方法?
- 答:使用property增强,只会生成最简单的getter/setter方法的声明和实现, 并不会对传入的数据进行过滤
- 如果想对传入的数据进行过滤, 那么我们就必须重写getter/setter方法
重写getter/setter方法有哪些注意点?
答:
- 如果重写了setter方法, 那么property就只会生成getter方法
- 如果重写了getter方法, 那么property就只会生成setter方法
- 如果同时重写了getter/setter方法, 那么property就不会自动帮我们生成私有的成员变量
16:property修饰符
增强@property使用修饰符后的的格式是什么?
答:
- 格式:
- @property(属性修饰符) 数据类型 变量名称;
@property 有哪些修饰符?各有什么作用?
- 答:readwrite: 代表既生成getter方法 , 也生成setter方法
- 默认情况下 @property就是readwrite的
- @property(readwrite) int age;
- ‘readonly: 代表只生成getter方法不生成setter方法’
- 可以给setter方法起别名@property(setter=tiZhong:) double weight;
- 可以给getter方法起别名@property(getter=isMarried) BOOL married;
17:静态数据类型和动态数据类型
静态数据类型的特点:
- 在编译时就知道变量的类型,
- 知道变量中有哪些属性和方法
- 在编译的时候就可以访问这些属性和方法,
- 并且如果是通过静态数据类型定义变量, 如果访问了不属于静态数据类型的属性和方法, 那么编译器就会报错
动态数据类型的特点:
- 在编译的时候编译器并不知道变量的真实类型, 只有在运行的时候才知道它的真实类型
- 并且如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错
id和NSObject * 的区别?
答:
- NSObject *是一个静态数据类型
- id 是一个动态数据类型
动态数据类型的应用场景?
- 答:动态类型主要用在多态, 可以减少代码量, 避免调用子类特有的方法需要强制类型转换
动态数据类型的弊端是什么?
- 答:由于动态数据类型可以调用任意方法, 所以有可能调用到不属于自己的方法, 而编译时又不会报错, 所以可能导致运行时的错误
判断数据类型的有哪些方法?(变量 修改为 对象)
- 答:为了避免动态数据类型引发的运行时的错误, 一般情况下如果使用动态数据类型保存一个对象, 在调用这个变量的方法之前会进行一次判断, 判断当前对象是否能够调用这个方法
18:构造方法
什么是构造方法?
答:
- 在OC中init开头的方法, 我们称之为构造方法
构造方法的用途?
答:
- 构造方法的用途: 用于初始化一个对象, 让某个对象一创建出来就拥有某些属性和值
如何实现构造方法?
答:
- 重写init方法, 在init方法中初始化成员变量
如何重写init方法?
答:重写init方法必须按照苹果规定的格式重写, 如果不按照规定会引发一些未知的错误
- (1)必须先初始化父类, 再初始化子类
- (2)必须判断父类是否初始化成功, 只有父类初始化成功才能继续初始化子类
- (3)返回当前对象的地址
1 - (instancetype)init 2 { 3 // 初始化父类 4 // 只要父类初始化成功 , 就会返回对应的地址, 如果初始化失败, 就会返回nil 5 // nil == 0 == 假 == 没有初始化成功 6 self = [super init]; 7 // 判断父类是否初始化成功 8 if (self != nil) { 9 // 初始化子类 10 // 设置属性的值 11 _age = 6; 12 } 13 // 返回地址 14 return self; 15 }
19:自定义构造方法
什么是自定义构造方法?为什么要自定义构造方法?
- (1)自定义构造方法就是自定义一个init方法
- (2)有时候我们需要在创建某个对象的时候,让对象的某些属性就具有值,这时候就需要传入一些参数给对象的属性,为了满足这个需求,就需要自定义构造方法
自定义构造方法的格式?
答:
- (1)一定是对象方法
- (2)一定返回id/instancetype
- (3)方法名称一定以init开头
-(instancetype)initWithAge:(int)age;
自定义构造方法在继承中有一个原则?
- 答:自己的事情自己做,属于谁的属性就由谁来进行操作
- 父类的属性交给父类的方法来处理,子类的方法处理子类自己独有的属性
自定义构造方法在子类,如何调用的父类构造方法的?
- 答:子类在重写自定构造方法时,一般使用super 调用父类的构造方法,先让父类将父类的属性进行初始化
1 - (instancetype)initWithAge:(int)age andName:(NSString *)name andNo:(int)no 2 { 3 if (self = [super initWithAge:age andName:name]) { 4 _no = no; 5 } 6 return self; 7 }
20:instancetype和id区别
instancetype和id区别?
答:
(1)id在编译的时候不能判断对象的真实类型
- instancetype在编译的时候可以判断对象的真实类型
(2)如果init方法的返回值是instancetype, 那么将返回值赋值给一个其它的对象会报一个警告
- 如果是在以前, init的返回值是id, 那么将init返回的对象地址赋值给其它对象是不会报错的
(3)id可以用来定义变量, 可以作为返回值, 可以作为形参
- instancetype只能用于作为返回值
instancetype 应用场景?
- 答:以后但凡自定义构造方法, 返回值尽量使用instancetype, 不要使用id
21:类工厂方法
什么是类工厂方法?
- 答:用于快速创建对象的类方法, 我们称之为类工厂方法
类工厂方法应用场景?
- 答:类工厂方法中主要用于 给对象分配存储空间和初始化这块存储空间
类工厂方法使用规范?
答:规范:
- 1.一定是类方法 +
- 2.方法名称以类的名称开头, 首字母小写
- 3.一定有返回值, 返回值是id/instancetype
- 4.在类工厂方法实现中,调用本类的构造方法,创建实例对象,并返回实例对象
自定义类工厂方法是苹果的一个规范, 一般情况下, 我们会给一个类提供自定义构造方法和自定义类工厂方法用于创建一个对象。
类工厂方法在继承中的注意点
以后但凡自定义类工厂方法, 在类工厂方法中创建对象一定要使用self来创建,一定不要使用类名来创建。
22:类的本质及存储细节
类的本质是什么?
答:
- (1)类其实也是一个对象, 这个对象会在这个类第一次被使用的时候创建
- (2)只要有了类对象, 将来就可以通过类对象来创建实例对象
- (3)实例对象中有一个isa指针, 指向创建自己的类对象
- (4)类对象中保存了当前对象所有的对象方法
- (5)当给一个实例对象发送消息的时候, 会根据实例对象中的isa指针去对应的类对象中查找
- (6)所有类对象的继承关系就是元类对象的继承关系
23:类的启动过程
1.load方法
- “load方法调用时间:”
- 只要程序启动就会将所有类的代码加载到内存中, 放到代码区
- “调用次数”
- load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次
2.initialize方法
- “initialize方法调用时间:”
- 当当前类第一次被使用的时候就会调用(创建类对象的时候)
- “调用次数”
24:SEL类型
SEL是什么类型?
答:
- SEL类型代表着方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系
SEL有什么作用?
答:
- (1)SEL类型的第一个作用, 配合对象/类来检查对象/类中有没有实现某一个方法
- (2)SEL类型的第二个作用, 配合对象/类来调用某一个SEL方法
- (3)配合对象将SEL类型作为方法的形参
哪个方法是用来检验对象是否实现了某个方法?
- 判断实例是否实现某个对象方法
- (BOOL)respondsToSelector: (SEL)selector
- 判断类是否实现某个类方法
- (BOOL)instancesRespondToSelector:(SEL)aSelector;
哪些方法是用来调用对象中SEL类型对应的方法?
答:
让对象执行某个方法
- - (id)performSelector:(SEL)aSelector;
- - (id)performSelector:(SEL)aSelector withObject:(id)object;
- - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
25:内存管理
什么是堆?什么是栈?
答:
- 栈(操作系统):由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈(先进后出);
- 堆(操作系统):一般由程序员分配释放,若程序员不释放,程序结束时可能由系统回收,分配方式类似于链表。
什么是内存管理?
答:
- 所谓内存管理, 就是对内存进行管理, 涉及的操作有
- 分配内存 : 比如创建一个对象, 会增加内存占用
- 清除内存 : 比如销毁一个对象, 能减小内存占用
内存管理的本质是什么?
答:
- OC对象存放于堆里面
- 非OC对象一般放在栈里面(栈内存会被系统自动回收)
26:MRC内存管理
什么是引用计数器?
- 答:每个OC对象都有自己的引用计数器,它是一个整数,表示有多少人正在用这个对象
引用计数器的作用?
答:
- (1)当使用alloc、new或者copy创建一个对象时,对象的引用计数器默认就是1
- (2)当对象的引用计数器为0时,对象占用的内存就会被系统回收
如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收(除非整个程序已经退出 )
怎么操作引用计数器?
答:
- 给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)
- 给对象发送一条release消息, 可以使引用计数器值-1
- 给对象发送retainCount消息, 可以获得当前的引用计数器值
- 需要注意的是: release并不代表销毁\回收对象, 仅仅是计数器-1
dealloc 方法的作用?
- 答:对象即将被销毁时系统会自动给对象发送一条dealloc消息 (因此, 从dealloc方法有没有被调用,就可以判断出对象是否被销毁)
重写dealloc方法有什么注意点?
- 答:重写dealloc方法, [super dealloc]一定要写到所有代码的最后
内存管理的原则?
答:
(1)谁创建谁release :
- 如果你通过alloc、new、copy或mutableCopy来创建一个对象,那么你必须调用release或autorelease
(2)谁retain谁release:
- 只要你调用了retain,就必须调用一次release
(3)总结:
- 有加就有减,曾经让对象的计数器+1,就必须在最后让对象的计数器-1
setter方法的内存管理
实现set方法内存管理有哪几步?
答:
- (1)retain需要使用的对象
- (2)release之前的对象
- (3)只有传入的对象和之前的不同才需要release和retain
1 - (void)setRoom:(Room *)room 2 { 3 // 只有房间不同才需用release和retain 4 if (_room != room) { 5 // 0ffe1 != 0ffe1 6 // 2.将以前的房间释放掉 -1 7 [_room release]; 8 // retain不仅仅会对引用计数器+1, 而且还会返回当前对象 9 _room = [room retain]; 10 } 11 }
property修饰符
1.readonly: 只会生成getter方法
- readwrite: 既会生成getter也会生成setter, 默认什么都不写就是readwrite
2.getter: 可以给生成的getter方法起一个名称
- setter: 可以给生成的setter方法起一个名称
3.retain: 就会自动帮我们生成getter/setter方法内存管理的代码
- assign: 不会帮我们生成set方法内存管理的代码, 仅仅只会生成普通的getter/setter方法, 默认什么都不写就是assign
4.多线程
- atomic :性能低(默认)
- nonatomic :性能高
- 在iOS开发中99.99%都是写nonatomic
property修饰符 有拿几点需要注意的问题?
答:
- 1.相同类型的property修饰符不能同时使用
- 2.不同类型的property修饰符可以多个结合在一起使用, 多个之间用,号隔开
- 3.iOS开发中只要写上property, 那么就立刻写上nonatomic
什么是循环retain?
- 答:
- 如果A对用要拥有B对象, 而B对应又要拥有A对象, 此时会形成循环retain
- 解决循环retain的方法,一边用retain一边用assign
27:autorelease 自动释放池
什么是自动释放池?
答:
- autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
自动释放池的优点是什么?
答:
- 不用再关心对象释放的时间
- 不用再关心什么时候调用release
简述自动释放池的原理?
答:
- autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该 Object放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。
自动释放池有哪些注意事项?
答:
- (1)在自动释放池中创建了对象, 一定要调用autorelease,才会将对象放入自动释放池中
- (2)一个程序中可以创建N个自动释放池, 并且自动释放池还可以嵌套
- (3)不要再自动释放池中使用比较消耗内存的对象, 占用内存比较大的对象
- (4)尽量不要再自动释放池中使用循环, 特别是循环的次数非常多, 并且还非常占用内存
- (5)千万不要写多次autorelease
- (6)一个alloc/new对应一个autorelease或者release
自动释放池是以什么形式存储的?
- 答: 如果存在多个自动释放池的时候, 自动释放池是以 “栈” 的形式存储在堆区
- 栈的特点: 先进后出
28:ARC内存管理
问题1:ARC的原理是什么?
- 答:当ARC开启时,编译器将自动在代码合适的地方插入retain, release和autorelease,而作为程序猿,完全不需要担心编译器会做错(除非开发者自己错用ARC了)。
ARC有什么优点?
答:
- 1.完全消除了手动管理内存的烦琐, 让程序猿更加专注于app的业务
- 2.基本上能够避免内存泄露
- 3.有时还能更加快速,因为编译器还可以执行某些优化
ARC的原则是什么?什么是强指针?什么是弱指针?
答:只要还有一个强指针变量指向对象,对象就会保持在内存中
(1)强指针
- 默认所有指针变量都是强指针
- 被__strong修饰的指针
(2)弱指针
- 被__weak修饰的指针
ARC下@property修饰符有哪些?
答:
- strong : 用于OC对象, 相当于MRC中的retain
- weak : 用于OC对象, 相当于MRC中的assign
- assign : 用于基本数据类型, 跟MRC中的assign一样
ARC中是怎么对对象进行内存管理的?
答:
- (1)ARC下单对象内存管理
- (2)ARC下,所有的指针都是强指针
- (3)ARC, A对象想拥有B对象, 那么就需要用一个强指针指向B对象
- (4)A对象不用B对象了, 什么都不需要做, 编译器会自动帮我们做
ARC怎么解决循环引用问题?
答:
- ARC和MRC一样, 如果A拥有B, B也拥有A, 那么必须一方使用弱指针
- 也就是说 一端用strong ,一端用weak
29:Category 分类
书写Category的格式?
答:
// 分类的声明
- @interface ClassName (CategoryName)
- NewMethod; //在类别中添加方法
- //不允许在类别中添加变量
- @end
- ClassName: 需要给哪个类扩充方法
- CategoryName: 分类的名称
- NewMethod: 扩充的方法
// 分类的实现
- @implementation ClassName(CategoryName)
- NewMethod
- … …
- @end
ClassName: 需要给哪个类扩充方法
- CategoryName: 分类的名称
- NewMethod: 扩充的方法
Category的作用?
答:
- (1)在不改变原来的类内容的基础上,为类增加一些方法。
- (2)一个庞大的类可以分模块开发,由多个人来编写,更有利于团队合作
分类,原来类或者父类中的方法调用的顺序?
- 答:先调用分类中的方法(最后参与编译的分类优先),再调用原来类中的方法,最后掉用父类中的方法
30:Extension 匿名扩展
什么是类扩展?
- 答:延展类别又称为扩展(Extension),Extension是Category的一个特例
类扩展格式?
答:
- 类扩展书写格式
- @interface 类名 ()
- @end
类扩展的作用是什么?
- 答:写在.m文件中,可以为某个类扩充一些私有的成员变量和方法
31:Block
什么是Block?
- 答:Block是iOS中一种比较特殊的数据类型,用来保存某一段代码
Block的作用?
- 答:Block用来保存某一段代码, 可以在恰当的时间再取出来调用
功能类似于函数和方法
- Block的格式?
- 答:Block的格式:
- 返回值类型 (^block变量名)(形参列表) = ^(形参列表) {
- };
32:协议
什么是协议?
- 答:其他语言有接口的概念,接口就是一堆方法的声明没有实现.
- OC中没有接口的概念,OC中的接口就是协议.
- 协议Protocol是由一系列的方法声明组成的
书写协议的格式?
答:
- 格式:
- @protocol 协议名称
- // 方法声明列表
- @end
一个类怎么遵循协议?
- 答:类遵守协议格式:
- @interface 类名 : 父类 <协议名称1, 协议名称2,…>
- @end
注意:
- (1)一个类可以遵守1个或多个协议
- (2)任何类只要遵守了Protocol,就相当于拥有了Protocol的所有方法声明
4.协议和继承有什么区别?
答:
- (1)继承之后默认就有实现, 而protocol只有声明没有实现
- (2)相同类型的类可以使用继承, 但是不同类型的类只能使用protocol
- (3)protocol可以用于存储方法的声明, 可以将多个类中共同的方法抽取出来, 以后让这些类遵守协议即可
什么是基协议?
- 答:基协议:是基协议,是最根本最基本的协议,其中声明了很多最基本的方法。
- 注意:建议每个新的协议都要遵守NSObject协议
6.协议有哪些注意事项?
答:
- (1)协议只能声明方法, 不能声明属性
- (2)父类遵守了某个协议, 那么子类也会自动遵守这个协议
- (3)在OC中一个类可以遵守1个或多个协议
- 注意: OC中的类只能有一个父类, 也就是说OC只有单继承
- (4)OC中的协议又可以遵守其它协议, 只要一个协议遵守了其它协议, 那么这个协议中就会自动包含其它协议的声明
7.协议中控制方法的能否实现的关键字是什么?各有什么作用?
- (1)注意: 如果没有使用任何关键字修饰协议中的方法, 那么该方法默认就是required的
- (2)注意:@required和@optional仅仅使用程序员之间交流, 并不能严格的控制某一个遵守该协议的类必须要实现该方法, 因为即便不是实现也不会报错, 只会报一个警告
- (3) @required
- 如果协议中的方法是@required的, 要求遵守协议的类实现@required所修饰的方法,如果没有实现该方法, 那么会报一个警告
- (4) @optional
- 如果协议中的方法是@optional的, 遵守协议的类可选择实现@optional所修饰的方法,如果没有实现该方法, 那么不会报警告
33:类型限定
什么是类型限定?
- 答:类型限定就是限定一个类必须遵守某个协议
类型限定的格式?
- 答:数据类型<协议名称> 变量名
- @property (nonatomic, strong) Wife *wife;
类型限定有什么注意点?
答:
- (1)类型限定是写在数据类型的右边的
- (2)虽然在接受某一个对象的时候, 对这个对象进行了类型限定(限定它必须实现某个协议),
- 但是并不意味着这个对象就真正的实现了该方法. 所以每次在调用对象的协议方法时应该进行一次验证
if ([self.wife respondsToSelector:@selector(cooking)]) { [self.wife cooking]; }
34:代理设计模式
代理模式的应用场景?
- (1)当A对象想监听B对象的一些变化时, 可以使用代理设计模式
- (2)当B对象发生一些事情, 想通知A对象的时候, 可以使用代理设计模式
- (3)当对象A无法处理某些行为的时候,想让对象B帮忙处理(让对象B成为对象A的代理对象)
用什么类型来接收遵守协议的代理对象?
- 答:使用id类型接收代理对象
简述一下协议的编写规范?
答:
- (1)一般情况下, 当前协议属于谁, 我们就将协议定义到谁的头文件中
- (2)协议的名称一般以它属于的那个类的类名开头, 后面跟上protocol或者delegate
- (3)协议中的方法名称一般以协议的名称protocol之前的作为开头
- (4)一般情况下协议中的方法会将触发该协议的对象传递出去
5.一般情况下一个类中的代理属于的名称叫做 delegate
6.当某一个类要成为另外一个类的代理的时候,
- 一般情况下在.h中用@protocol 协议名称;告诉当前类 这是一个协议.
- 在.m中用#import真正的导入一个协议的声明
35:Foundation
什么是框架?
答:
- 众多功能\API的集合.
- 框架是由许多类、方法、函数、文档按照一定的逻辑组织起来的集合,以便使研发程序变得更容易,在OS X下的Mac操作系统中大约有80个框架为所有程序开发奠定基础的框架称为Foundation 框架
Foundation 框架有什么作用?
答:
- 1.Foundation框架是Mac\iOS中其他框架的基础
- 2.Foundation框架包含了很多开发中常用的数据类型:结构体,枚举, 类
什么是NSString?
- 答:一个NSString对象就代表一个字符串(文字内容)
- 一般称NSString为字符串类
如何创建NSString对象?有几种方法创建一个NSString字符串?
答:
(1)通过@”“直接创建
- 如果通过@”“创建字符串, 那么会将字符串放到常量区中
- 如果是字符串常量, 那么只要内容相同 , 不会重复创建
- NSString *str1 = @"james";
(2)通过alloc或者类工厂方法创建
- 如果是通过alloc或者类工厂方法创建, 那么会将字符串放到堆区中
- NSString *str2 = [[NSString alloc] initWithString:@"james"];
- NSString *str3 = [NSString stringWithFormat:@"jack"];
如何将字符串写入到文件中?
- NSString *str = @"iOS";
- NSString *path2 = @"/Users/james/Desktop/abc.txt";
- BOOL flag = [str writeToFile:path2 atomically:YES encoding:NSUTF8StringEncoding error:nil];
- NSLog(@"flag = %i", flag);
6.什么是URL?
答:
- (1)URL的全称是Uniform Resource Locator(统一资源定位符)
- (2)URL是互联网上标准资源的地址
- (3)互联网上的每个资源都有一个唯一的URL,它包含的信息指出资源的位置
- (4)根据一个URL就能找到唯一的一个资源
7.写URL格式?
- 答: URL = 协议头://主机地址/路径
8.如何创建URL
答:
(1)通过alloc 或者类工厂方法创建
- NSURL *url = [NSURL URLWithString:@"file:///Users/james/Desktop/str.txt"];
- NSURL *url = [[NSURL alloc] initWithString:@"file:///Users/james/Desktop/str.txt"];
(2)通过文件路径创建(默认就是file协议的)
- NSURL *url = [NSURL fileURLWithPath:@"/Users/james/Desktop/str.txt"];
9.如何获取本地路径的信息?获取本地路径信息的方法有什么注意点?
答:获取本地路径信息–fileURLWithPath
方法一:
(1)字符串保存路径
- NSString *path = @"file://192.168.13.10/Users/james/Desktop/note/ja.txt";
- NSLog(@"url编码前: %@", path);
(2)将路径中中文转换为UTF-8编码格式
- path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSLog(@"url编码后: %@", path);
方法二:
(1)字符串保存路径,如果访问本机的文件, 可以省略主机地址
- NSString *path = @”file:///Users/james/Desktop/note/ja.txt”;
- NSLog(@”url编码前: %@”, path);
(2)将路径中中文转换为UTF-8编码格式
- path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSLog(@”url编码后: %@”, path);
- NSURL *url = [NSURL URLWithString:path];
10.获取本地路径的信息
1 NSURL *url = [NSURL fileURLWithPath:@"/Users/james/Desktop/note/ja.txt"]; 2 NSError *error = nil; 3 NSString *str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; 4 if (error == nil) { 5 NSLog(@"str = %@", str); 6 }else{ 7 NSLog(@"error = %@", [error localizedDescription]); 8 }
注意点:
- URLWithString: 方法不支持中文,所以无法成功创建URL,必须先对路径字符串进行编码
- fileURLWithPath: 方法支持中文,并且省略协议头,但是只能获取本地方法
11.如何获取网络路径的信息?
答:
- 获取网络路径的信息–URLWithString
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"]; NSString *str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; NSLog(@"str = %@", str);
12.如何将信息写入到指定文件?
答:
方法一:
1 NSString *str = @"james"; 2 NSString *path = @"file:///Users/james/Desktop/未命名文件夹/abc.txt"; 3 path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 4 NSURL *url = [NSURL URLWithString:path]; 5 [str writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:nil];
方法二:
1 NSString *str = @"james"; 2 NSString *path = @"/Users/james/Desktop/未命名文件夹/abc.txt"; 3 NSURL *url = [NSURL fileURLWithPath:path]; 4 [str writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:nil];
总结:
- 1.如果多次往同一个文件中写入内容,那么后一次的会覆盖前一次的 \
- 2.方法名中没有file,路径中要添加上file协议头,如果方法名中有file,路径中就不需要file协议头
36:NSString
如何比较两个字符串的”内容”是否相同?
答:
- BOOL flag = [str1 isEqualToString:str2];
- NSLog(@”flag = %i”, flag);
如何比较两个字符串的”地址”是否相同?
- flag = (str1 == str2);
- NSLog(@”flag = %i”, flag);
如何比较字符串的大小?
- 答:使用方法compare:
- NSOrderedAscending 前面的小于后面的
- NSOrderedSame, 两个字符串相等
- NSOrderedDescending 前面的大于后面的
如何忽略大小写进行比较?
- [str1 caseInsensitiveCompare:str2];
如何判断字符串是否以什么结尾的?本质是什么?
答:
- 本质就是从字符串的最后一个字符开始匹配, 只要不匹配就返回NO
1 if ([str hasSuffix:@".gif"]) { 2 NSLog(@"动态图片"); 3 }else{ 4 NSLog(@"不是动态图片"); 5 }
6.如何获取指定范围内的字符串?
答:
(1)动态获取截取的起始位置
- NSUInteger location = [str rangeOfString:@”>”].location + 1;
(2)动态获取截取的长度
1 NSUInteger length = [str rangeOfString:@"</"].location - location; 2 NSRange range = NSMakeRange(location, length); 3 NSString *newStr = [str substringWithRange:range]; 4 NSLog(@"str = %@", str); 5 NSLog(@"newStr = %@", newStr);
7.如何替换字符串中的部分字符?
答:
- 需求: 将&符号替换为/
1 NSString *str = @"http:&&www.weibo.iosjames.com&img&james.gif"; 2 ..OccurrencesOfString: 要替换谁 3 // withString: 用谁替换 4 NSString *newStr = [str stringByReplacingOccurrencesOfString:@"&" withString:@"/"]; 5 NSLog(@"newStr = %@", newStr);
8.如何对字符串首位进行处理?
答:
1 NSString *str = @"HTTP://www.baidu.com/img/james.GIF"; 2 NSCharacterSet *set = [NSCharacterSet uppercaseLetterCharacterSet]; 3 NSString *newStr = [str stringByTrimmingCharactersInSet:set]; 4 NSLog(@"newStr = |%@|", newStr);
9.如何给文件路径添加一个目录?
答:
- 本质就是在字符串的末尾加上一个/ 和指定的内容
- 注意: 如果路径后面已经有了/, 那么就不会添加了
- 如果路径后面有多个/, 那么会自动删除多余的/, 只保留一个
1 NSString *newStr = [str stringByAppendingPathComponent:@"james"]; 2 NSLog(@"%@", newStr);
10.如何获取路径中文件的扩展名?
答:
- 本质就是从字符串的末尾开始查找., 截取第一个.后面的内容
NSString *newStr = [str pathExtension]; NSLog(@"%@", newStr);
11.如何删除路径中文件的扩展名?
答:
- 本质就是从字符串的末尾开始查找.,删除第一个.和.后面的内容
NSString *newStr = [str stringByDeletingPathExtension]; NSLog(@"%@", newStr);
12.如何给文件路径添加一个扩展名?
答:
本质就是在字符串的末尾加上一个.和指定的内容
NSString *newStr = [str stringByAppendingPathExtension:@"jpg"]; NSLog(@"%@", newStr);
13.如何将将字符串转换为大写?
答:
NSString *newStr = [str uppercaseString]; NSLog(@"%@", newStr);
14.如何将字符串转换为小写?
答:
NSString *newStr2 = [newStr lowercaseString]; NSLog(@"%@", newStr2);
15.如何将字符串的首字符转换为大写
答:
NSString *newStr = [str capitalizedString]; NSLog(@"%@", newStr); NSMutalbleString
NSMutableString和NSString的区别?
答:
- (1)NSString是不可变的, 里面的文字内容是不能进行修改的
- (2)NSMutableString是可变的, 里面的文字内容可以随时更改
- (3)NSMutableString能使用NSString的所有方法
什么是可变字符串?什么是不可变字符串?
答:
- 不可变字符串:指的是字符串在内存中占用的存储空间固定,并且存储的内容不能发生变化
- 可变字符串:指的是字符串在内存中占用的存储空间可以不固定,并且存储的内容可以被修改
在字符串后面添加一段字符串?
答:
[strM appendString:@"/image"]; NSLog(@"strM = %@", strM);
4.如何删除字符串中的字符?
- 技巧: 在开发中, 我们经常利用rangeOfString和deleteCharactersInRange方法配合起来删除指定的字符串
NSRange range = [strM rangeOfString:@"xx"]; [strM deleteCharactersInRange:range]; NSLog(@"strM = %@", strM);
5.如何在某个字符前面插入love这个单词?
答:
- insertString : 需要插入的字符串
- atIndex: 从哪里开始插入
NSRange range = [strM rangeOfString:@"xx"]; [strM insertString:@"love" atIndex:range.location]; NSLog(@"strM = %@", strM);
37:NSArray
什么是NSArray?
- 答:NSArray是OC中的数组类,开发中建议尽量使用NSArray替代C语言中的数组
NSArray有哪些使用注意?
答:
- (1)只能存放任意OC对象, 并且是有顺序的
- (2)不能存储非OC对象, 比如int\float\double\char\enum\struct等
- (3)它是不可变的,一旦初始化完毕后,它里面的内容就永远是固定的, 不能删除里面的元素, 也不能再往里面添加元素
- (4)NSArray使用NSLog()打印,输出的是小括号的格式。
- (5)NSArray中不能存储nil,因为NSArray认为nil是数组的结束(nil是数组元素结束的标记)。nil就是0。0也是基本数据类型,不能存放到NSArray中。
NSArray有哪些常用方法?
答:
- - (NSUInteger)count; 获取集合元素个数
- - (id)objectAtIndex:(NSUInteger)index; 获得index位置的元素
- - (BOOL)containsObject:(id)anObject; 是否包含某一个元素
- - (id)lastObject; 返回最后一个元素
- - (id)firstObject; 返回最后一个元素
- - (NSUInteger)indexOfObject:(id)anObject; 查找anObject元素在数组中的位置(如果找不到,返回-1)
- - (NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range;
在range范围内查找anObject元素在数组中的位置
4.书写NSArray简写形式?
答:
NSArray *arr = [NSArray arrayWithObjects:@"ldda", @"james", @"jjj", nil]; NSArray *arr = @[@"james", @"jasc", @"jjj"];
获取数组元素的简写
NSLog(@"%@", [arr objectAtIndex:0]); NSLog(@"%@", arr[0]);
5.如何使用增强for循环,遍历NSArray数组?
答:
- 逐个取出arr中的元素, 将取出的元素赋值给obj
- 注意: obj的类型可以根据数组中元素的类型来写, 不一定要写NSObject
- for (NSString *obj in arr) {
- NSLog(@”obj = %@”, obj);
- }
6.如何使用OC数组的迭代器来遍历数组?
答:
- 每取出一个元素就会调用一次block
- 每次调用block都会将当前取出的元素和元素对应的索引传递给我们
- obj就是当前取出的元素, idx就是当前元素对应的索引
- stop用于控制什么时候停止遍历
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (idx == 1) { *stop = YES; } NSLog(@"obj = %@, idx = %lu", obj, idx); }];
7.如何对数据进行排序?NSArray *arr = @[@10, @20, @5, @7, @15];
- NSLog(@”排序前: %@”, arr);意:**
- 想使用compare方法对数组中的元素进行排序, 那么数组中的元素必须是Foundation框架中的对象, 也就是说不能是自定义对象
NSArray *newArr = [arr sortedArrayUsingSelector:@selector(compare:)]; NSLog(@"排序后: %@", newArr);
8.如何将数组写入文件中?
答:
NSArray *arr = @[@"eee", @"aaa", @"jjj"]; BOOL flag = [arr writeToFile:@"/Users/james/Desktop/abc.plist" atomically:YES]; NSLog(@"flag = %i", flag);
9.将数组写入文件中,有什么注意事项?
答:
注意(1):
- 如果将一个数组写入到文件中之后, 本质是写入了一个XML文件
- 在iOS开发中一般情况下我们会将XML文件的扩展名保存为plist
注意(2)
- writeToFile只能写入数组中保存的元素都是Foundation框架中的类创建的对象, 如果保存的是自定义对象那么不能写入
10.如何从文件中读取数据到NSArray中?
答:
- 从文件中读取一个数组,此方法在字典转模型中,经常用到
NSArray *newArray = [NSArray arrayWithContentsOfFile:@"/Users/james/Desktop/abc.plist"]; NSLog(@"%@", newArray);
38:NSMutableArray
1.什么是可变数组?和NSArray有什么区别?
答:
- (1)NSMutableArray是NSArray的子类
- (2)NSArray是不可变的,一旦初始化完毕后,它里面的内容就永远是固定的, 不能删除里面的元素, 也不能再往里面添加元素
- (3)NSMutableArray是可变的,随时可以往里面添加\更改\删除元素
2.如何创建一个空的数组?创建可变数组有什么注意点?
- NSMutableArray *arrM = [NSMutableArray array];
- NSMutableArray *arrM = [[NSMutableArray alloc] init];
注意:
- 不能通过@[]来创建一个可变数组, 因为@[]创建出来的是一个不可变的数组
3.如何给可变数组增加内容?
答:
方法一:
- [arrM addObject:@"james"];
方法二:
- 将指定数组中的元素都取出来, 放到arrM中 \
- 并不是将整个数组作为一个元素添加到arrM中
[arrM addObjectsFromArray:@[@"james", @"jjj"]];
注意:
- 以下是将整个数组作为一个元素添加
[arrM addObject:@[@"ss", @"jjj"]]; NSLog(@"%@", arrM);
4.如何给可变数组插入内容?
答:
[arrM insertObject:@"xcq" atIndex:1]; NSRange range = NSMakeRange(2, 2); NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:range];
插入一组数据, 指定数组需要插入的位置, 和插入多少个
[arrM insertObjects:@[@”A”, @”B”] atIndexes:set];
5.如何删除可变数组中的内容?
答:
删除
- [arrM removeObjectAtIndex:0]
删除数组中最后一个元素
- [arrM removeLastObject];
删除index位置的元素
- [arrM removeObject:@"A"];
6.如何替换可变数组中的内容?
答
- [arrM replaceObjectAtIndex:1 withObject:@"M"];
//简写:
- arrM[0] = @"ZS";
- NSLog(@"%@", arrM);
7.如何获取可变数组中的内容?
答:
- NSLog(@"%@", [arrM objectAtIndex:0]);
39:NSDictionary
什么是字典?
- 答:OC中的NSDictionary:根据key找到value,字典中存储的东西都是键值对
如何创建字典?
答:
方法一:
- NSDictionary *dict = [NSDictionary dictionaryWithObject:@"james" forKey:@"name"];
- NSString *name = [dict objectForKey:@"name"];
- NSLog(@"name = %@", name);
方法二:
- 注意: key和value 是一一对应
- NSDictionary *dict = [NSDictionary dictionaryWithObjects:@[@"james", @"22", @"1.75"] forKeys:@[@"name", @"age", @"height"]];
- NSLog(@"%@ %@ %@", [dict objectForKey:@"name"], [dict objectForKey:@"age"], [dict objectForKey:@"height"]);
方法三:简写:
- NSDictionary *dict = @{key:value};
- NSDictionary *dict = @{@"name": @"james"};
- NSLog(@"%@", dict[@"name"]);
NSDictionary *dict = @{@"name":@"james", @"age":@"30", @"height":@"1.75"}; NSLog(@"%@ %@ %@", dict[@"name"], dict[@"age"], dict[@"height"]);
3.如何对字典进行遍历?
答:
- NSDictionary *dict = @{@"name":@"james", @"age":@"22", @"height":@"1.75"};
//获取字典中key和value的个数, 在字典中key称之为键, value称之为值
- NSLog(@"count = %lu", [dict count]);
方法一:老式for循环写法
for (int i = 0; i < dict.count; ++i) { // 获取字典中所有的key NSArray *keys = [dict allKeys]; // 取出当前位置对应的key // NSLog(@"%@", keys[i]); NSString *key = keys[i]; NSString *value = dict[key]; NSLog(@"key = %@, value = %@", key, value); }
方法二:增强for循环写法
如何通过forin遍历字典, 会将所有的key赋值给前面的obj
for (NSString *key in dict) { NSLog(@"%@", key); NSString *value = dict[key]; NSLog(@"key = %@, value = %@", key, value); }
方法三:OC字典的迭代器来遍历
- [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
- NSLog(@"key = %@, value = %@", key, obj);
- }];
4.如何对字典文件进行读写?
答:
(1)将字典数据写入文件中
NSDictionary *dict = @{@"name":@"james", @"age":@"22", @"height":@"1.75"}; [dict writeToFile:@"/Users/james/Desktop/info.plist" atomically:YES];
(2)从文件中读取字典数据
注意: 字典和数组不同, 字典中保存的数据是无序的
NSDictionary *newDict = [NSDictionary dictionaryWithContentsOfFile:@"/Users/james/Desktop/info.plist"]; NSLog(@"%@", newDict); NSArray *arr = @[@10, @20, @30, @5]; [arr writeToFile:@"/Users/james/Desktop/abc.plist" atomically:YES];
40:NSMutableDictionary
如何创建一个空的可变字典?
答:
- NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
如何给可变字典添加键值对?
答:
- [dictM setObject:@"lnj" forKey:@"name"];
简写:
- dictM[@"name"] = @"james";
会将传入字典中所有的键值对取出来添加到dictM中
- [dictM setValuesForKeysWithDictionary:@{@"age":@"30", @"height":@"1.75"}];
3.如何删除可变字典键值对?
答:
- [dictM removeObjectForKey:@"name"];
- [dictM removeObjectsForKeys:@[@"age", @"height"]];
4.如何修改可变字典中的键值对?
答:利用setObject方法给同名的key赋值, 那么新值会覆盖旧值
- [dictM setObject:@"88" forKey:@"age"];
简写:
- dictM[@"age"] = @"88";
5.使用可变字典有什么注意事项?
答:
(1)不能使用@{}来创建一个可变的字典
- NSMutableDictionary *dictM = @{@”name”:@”james”};//编译就会报错
- [dictM setObject:@”30” forKey:@”age”];
(2)
- 如果是不可变字典, 那么key不能相同
- 如果是不可变字典出现了同名的key, 那么后面的key对应的值不会被保存
- 如果是在可变数组中, 后面的会覆盖前面的
NSDictionary *dict = @{@"name":@"lkl", @"name":@"lll"}; NSLog(@"dict = %@", dict); NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithObjects:@[@"lkl", @"lll"] forKeys:@[@"name", @"name"]]; NSLog(@"dict = %@", dictM);
6.NSArray和NSDictionary的区别?
答:
- NSArray是有序的,NSDictionary是无序的
- NSArray是通过下标访问元素,NSDictionary是通过key访问元素
41:NSNumber
1.NSNumber的应用场景?
答:
- NSNumber可以将基本数据类型包装成对象,这样就可以间接将基本数据类型存进NSArray\NSDictionary中
2.如何将基本数据类型转换为对象类型?
答:
- int age = 10;
- double number= 5.1;
- int value = 6;
- NSNumber *ageN = [NSNumber numberWithInt:age];
- NSNumber *numberN = [NSNumber numberWithDouble:number];
- NSNumber *valueN = [NSNumber numberWithInt:value];
- NSArray *arr = @[ageN, numberN, valueN];
- NSLog(@"arr = %@", arr);
3.如何将对象类型转换为基本数据类型?
答:
- int temp = [ageN intValue];
- double temp = [numberN doubleValue];
4.如何将基本数据类型转换对象类型简写?有什么注意点?
答:
注意: 如果传入的是变量那么必须在@后面写上(), 如果传入的常量, 那么@后面的()可以省略
NSNumber *temp = @(number); NSNumber *temp =@10.10; NSLog(@"%@", temp); NSValue
1.NSValue的应用场景?
答:
- (1)NSNumber是NSValue的子类, 但NSNumber只能包装数字类型
- (2)NSValue可以包装任意值
- 因此, 可以用NSValue将结构体包装后,加入NSArray\NSDictionary中
2.如何利用NSValue包装常用的结构体?
答:
CGPoint point = NSMakePoint(10, 20); NSValue *value = [NSValue valueWithPoint:point]; NSArray *arr = @[value]; NSLog(@"%@", arr);
3.如何利用NSValue包装自定义的结构体?
答:
typedef struct{ int age; char *name; double height; }Person; Person p = {30, "lld", 1.75};
42:NSDate
1.NSDate的应用场景?
- 答:NSDate可以用来表示时间, 可以进行一些常见的日期\时间处理
- [NSDate date]返回的就是当前0时区的时间
2.如何获取当前时区的时间?
答:
(1) date方法创建的时间对象, 对象中就保存了当前的时间
- NSDate *now = [NSDate date];
(2)获取当前所处的时区
- NSTimeZone *zone = [NSTimeZone systemTimeZone];
(3)获取当前时区和指定时区时间的时间差
- NSInteger seconds = [zone secondsFromGMTForDate:now];
- // NSLog(@"seconds = %lu", seconds);
(4)计算出当前时区的时间
- NSDate *newDate = [now dateByAddingTimeInterval:seconds];
- NSLog(@"newDate = %@", newDate);
3.如何格式化时间?
答:
NSDate 转 NSString
(1)创建时间
- NSDate *now = [NSDate date];
(2)创建时间格式化
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
(3)指定格式
- formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
(4)格式化时间
- NSString *str = [formatter stringFromDate:now];
- NSLog(@"%@", str);
NSString 转 NSDate
(1)获取时间
- NSString *str = @"2015-08-20 07:05:26 +0000";
2.创建时间格式化
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
注意:
- 如果是从NSString格式化为NSDate, 那么dateFormat的格式, 必须和字符串中的时间格式一致, 否则可能转换失败
(3)指定格式
- formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
4.格式化日期
- NSDate *date = [formatter dateFromString:str];
- NSLog(@"%@", date);
43:NSCalendar
1.如何获取当前时间的年月日时分秒?
答:
(1)获取当前时间
- NSDate *now = [NSDate date];
- NSLog(@"now = %@", now);
获得NSCalendar 日历对象
- NSCalendar *calendar1 = [NSCalendar currentCalendar];
// 利用日历类从当前时间对象中获取 年月日时分秒(单独获取出来)
// components: 参数的含义是, 问你需要获取什么?
// 一般情况下如果一个方法接收一个参数, 这个参数是是一个枚举 , 那么可以通过|符号, 连接多个枚举值
- NSCalendarUnit type = NSCalendarUnitYear |
- NSCalendarUnitMonth |
- NSCalendarUnitDay |
- NSCalendarUnitHour |
- NSCalendarUnitMinute |
- NSCalendarUnitSecond;
- NSDateComponents *cmps = [calendar1 components:type fromDate:now];
- NSLog(@"year = %ld", cmps.year);
- NSLog(@"month = %ld", cmps.month);
- NSLog(@"day = %ld", cmps.day);
- NSLog(@"hour = %ld", cmps.hour);
- NSLog(@"minute = %ld", cmps.minute);
- NSLog(@"second = %ld", cmps.second);
2.如何获取当前时间和指定时间的时间差?
答:
1.过去的一个时间
- NSString *str = @"2015-06-29 07:05:26 +0000";
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
- formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
- NSDate *date = [formatter dateFromString:str];
2.当前的时间
- NSDate *now = [NSDate date];
- NSLog(@"date = %@", date);
- NSLog(@"now = %@", now);
3.比较两个时间
- NSCalendar *calendar = [NSCalendar currentCalendar];
- NSCalendarUnit type = NSCalendarUnitYear |
- NSCalendarUnitMonth |
- NSCalendarUnitDay |
- NSCalendarUnitHour |
- NSCalendarUnitMinute |
- NSCalendarUnitSecond;
- NSDateComponents *cmps = [calendar components:type fromDate:date toDate:now options:0];
- NSLog(@"%ld年%ld月%ld日%ld小时%ld分钟%ld秒钟", cmps.year, cmps.month, cmps.day, cmps.hour, cmps.minute, cmps.second);
44:NSFileManager
1.什么是NSFileManager?如何获取NSFileManager 对象?
答:
- NSFileManager是用来管理文件系统的
- 它可以用来进行常见的文件\文件夹操作
- NSFileManager *manager = [NSFileManager defaultManager];
2.如何判断一个文件或者文件夹是否存在?
答:
BOOL flag = [manager fileExistsAtPath:@"/Users/james/Desktop/video/01-NSArray.text"]; NSLog(@"flag = %i", flag);
3.如何获取文件或文件夹的属性?
答:
NSDictionary *info = [manager attributesOfItemAtPath:@"/Users/james/Desktop/video/acn.mp4" error:nil]; NSLog(@"info = %@", info);
4.如何获取文件夹中所有的文件?
答:
注意:
NSArray *res = [manager contentsOfDirectoryAtPath:@"/Users/xiaomage/Desktop/video" error:nil]; NSLog(@"res = %@", res);
获取当前文件夹下所有的文件, 能获取子文件夹下面的文件
NSArray *res = [manager subpathsAtPath:@"/Users/james/Desktop/video"]; NSArray *res = [manager subpathsOfDirectoryAtPath:@"/Users/james/Desktop/video" error:nil]; NSLog(@"res = %@", res);
5.如何创建文件夹?
答:
- createDirectoryAtPath: 告诉系统文件夹需要创建到什么位置
- withIntermediateDirectories: 如果指定的文件中有一些文件夹不存在, 是否自动创建不存在的文件夹
- attributes: 指定创建出来的文件夹的属性
- error: 是否创建成功, 如果失败会给传入的参数赋值
- 注意: 该方法只能用于创建文件夹, 不能用于创建文件
BOOL flag = [manager createDirectoryAtPath:@"/Users/james/Desktop/abc/llq" withIntermediateDirectories:YES attributes:nil error:nil]; NSLog(@"%i", flag);
6.如何创建文件?
- createFileAtPath: 指定文件创建出来的位置
- contents : 文件中的内容
- attributes: 创建出来的文件的属性
- NSData : 二进制数据
注意: 该方法只能用于创建文件, 不能用于创建文件夹
45:Copy
1.使用copy功能的前提是什么?
答:
- copy : 需要遵守NSCopying协议,实现copyWithZone:方法
- 使用mutableCopy的前提
- 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法
2.如何使用copy功能?
答:
- 一个对象可以调用copy或mutableCopy方法来创建一个副本对象
- copy : 创建的是不可变副本(如NSString、NSArray、NSDictionary)
- mutableCopy :创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
3.copy基本原则?
答:
- 修改源对象的属性和行为,不会影响副本对象
- 修改副本对象的属性和行为,不会影响源对象
4.为什么通过不可变对象调用了copy方法, 不会生成一个新的对象?
答:
- 因为原来的对象是不能修改的, 拷贝出来的对象也是不能修改的
- 既然两个都不能修改, 所以永远不能影响到另外一个对象, 那么已经符合需求
- 所以: OC为了对内存进行优化, 就不会生成一个新的对象
46:copy内存管理
1.浅复制(浅拷贝,指针拷贝,shallow copy)
- 源对象和副本对象是同一个对象
- 源对象(副本对象)引用计数器+1,相当于做一次retain操作
- 本质是:没有产生新的对象
2.深复制(深拷贝,内容拷贝,deep copy)
- 源对象和副本对象是不同的两个对象
- 源对象引用计数器不变,副本对象计数器为1(因为是新产生的)
- 本质是:产生了新的对象
47:copy和Property
1.@property中的copy的作用是什么?
答:
- (1)防止外界修改内部的数据
- (2)可以使用copy保存block, 这样可以保住block中使用的外界对象的命
- block默认存储在栈中, 栈中的block访问到了外界的对象, 不会对对象进行retain
2.@property内存管理原则?
答:
MRC
- 1> copy : 只用于NSString\block
- 2> retain : 除NSString\block以外的OC对象
- 3> assign :基本数据类型、枚举、结构体(非OC对象),当2个对象相互引用,一端用retain,一端用assign
ARC
- 1> copy : 只用于NSString\block
- 2> strong : 除NSString\block以外的OC对象
- 3> weak : 当2个对象相互引用,一端用strong,一端用weak
- 4> assgin : 基本数据类型、枚举、结构体(非OC对象)
3.如何解决block中的循环引用?
答:如果对象中的block又用到了对象自己, 那么为了避免内存泄露, 应该将对象修饰为__block
1 __block Person *p = [[Person alloc] init]; // 1 2 p.name = @"james"; 3 NSLog(@"retainCount = %lu", [p retainCount]); 4 p.pBlock = ^{ 5 NSLog(@"name = %@", p.name); // 2 6 }; 7 NSLog(@"retainCount = %lu", [p retainCount]); 8 p.pBlock();
48:自定义类实现Copy
1.自定义类如何实现copy操作?
答:
- (1)以后想让自定义的对象能够被copy只需要遵守NSCopying协议
- (2)实现协议中的- (id)copyWithZone:(NSZone *)zone
- (3)在- (id)copyWithZone:(NSZone *)zone方法中创建一个副本对象, 然后将当前对象的值赋值给副本对象即可
1 - (id)copyWithZone:(NSZone *)zone 2 { 3 // 1.创建一个新的对象 4 Person *p = [[[self class] allocWithZone:zone] init]; 5 // 2.设置当前对象的内容给新的对象 6 p.age = _age; 7 p.name = _name; 8 // 3.返回新的对象 9 return p; 10 } 11 - (id)mutableCopyWithZone:(NSZone *)zone 12 { 13 // 1.创建一个新的对象 14 Person *p = [[[self class] allocWithZone:zone] init]; 15 // 2.设置当前对象的内容给新的对象 16 p.age = _age; 17 p.name = _name; 18 // 3.返回新的对象 19 return p; 20 }
49:单例ARC和MRC写法
1.什么是单例模式?
- 答:类的对象成为系统中唯一的实例,提供一个访问点,供客户类 共享资源
- 单例就是无论怎么创建都只能有一个实例对象
2.什么情况下使用单例?
答:
- (1)类只能有一个实例,而且必须从一个为人熟知的访问点对其进行访问,比如工厂方法。
- (2)这个唯一的实例只能通过子类化进行扩展,而且扩展的对象不会破坏客户端代码。
3.创建单例对象的方法一般以什么开头?
答:
- (1)一般情况下创建一个单利对象都有一个与之对应的类方法
- (2)一般情况下用于创建单利对象的方法名称都以share开头, 或者以default开头
单例在多线程的应用?
答:多线程中的单例
1 + (instancetype)allocWithZone:(struct _NSZone *)zone 2 { 3 // 以下代码在多线程中也能保证只执行一次 4 static dispatch_once_t onceToken; 5 dispatch_once(&onceToken, ^{ 6 _instance = [[super allocWithZone:zone] init]; 7 }); 8 return _instance; 9 }
重点总结
一、category 分类
在C++中有强大的多重继承,而在oc中方法都是单继承的,为了模块化开发,便于团队开发,oc中有其他语言所没有的category(分类,类目,类别)和class Exetension(类扩展)等语法,可以在不修改原来类的基础上, 为这个类扩充一些方法.
Category的格式
1.在.h文件中声明类别
- (1)新添加的方法必须写在 @interface 与 @end之间
- (2)ClassName 现有类的类名(要为哪个类扩展方法) +
- (3)CategoryName 待声明的类别名称
- (4)NewMethod 新添加的方法
@interface ClassName (CategoryName) NewMethod; //在类别中添加方法 //不允许在类别中添加变量 @end
注意:
- 不允许在声明类别的时候定义变量
在.m文件中实现类别:
- (1)新方法的实现必须写在@ implementation与@end之间
- (2)ClassName 现有类的类名
- (3)CategoryName 待声明的类别名称
- (4)NewMethod 新添加的方法的实现
category使用注意:
- 1. 分类只能增加方法, 不能增加成员变量
- 2. 分类中写property只会生成方法声明
- 3. 分类可以访问原来类中的成员变量
- 4. 如果分类和原来类出现同名的方法, 优先调用分类中的方法, 原来类中的方法会被忽略
分类的编译的顺序
- 方法调用顺序(从高到低) 分类 –>原来类 –>父类
- 分类应用场景:开发中经常使用的方法,可以抽出一个分类,团队开发中,多人共同开发,可以写多个分类,最后进行融合。
二、Exetention 匿名扩展
1.匿名扩展定义
- 延展类别又称为扩展(Extendsion),Extension是Category的一个特例
- 可以为某个类扩充一些私有的成员变量和方法写在.m文件中
- 英文名是Class Extension
2.扩展书写格式
在.m文件中
- @interface 类名 ()
- {
- int _age;
- }
- - (void)say;
- @end
三、Block
1.Block简介
Block是iOS中一种比较特殊的数据类型
Block是苹果官方特别推荐使用的数据类型, 应用场景比较广泛。
Block应用场景:
- 动画
- 多线程
- 集合遍历
- 网络请求回调
2.Block的作用
- 用来保存某一段代码, 可以在恰当的时间再取出来调用
- 功能有点类似于函数和方法。
3.Block的定义格式:
返回值类型 (^block变量名)(形参列表) = ^(形参列表) {
};
(1)无返回值,无形参
- void(^Myblock)() = ^{
- NSLog(@”Hello”);
- };
(2)有返回值,有形参
- int(^MyBlock)(int,int) = ^(int a,int b){
- return a +b;
- };
(3)无返回值,有形参
- void(^MyBlock)(int) = ^(int num){
- for (int i = 0,i
三、Protocol 协议
1.protocol 基本概念
Protocol翻译过来, 叫做”协议”
- (1)在写java的时候都会有接口interface这个概念,接口就是一堆方法的声明没有实现,而在OC里面Interface是一个类的头文件的声明,并不是真正意义上的接口的意思,在OC中接口是由一个叫做协议的protocol来实现的
- protocol它可以声明一些必须实现的方法和选择实现 的方法。这个和java是完全不同的。
2.Protocol的作用:
- 用来声明一些方法,也就说, 一个Protocol是由一系列的方法声明组成的。
3.protocol 语法格式
(1)Protocol的定义
- @protocol 协议名称
- // 方法声明列表
- @end
(2)类遵守协议
- 一个类可以遵守1个或多个协议
- 任何类只要遵守了Protocol,就相当于拥有了Protocol的所有方法声明.
4.protocol和继承区别
- 继承之后默认就有实现, 而protocol只有声明没有实现
- 相同类型的类可以使用继承, 但是不同类型的类只能使用protocol
- protocol可以用于存储方法的声明, 可以将多个类中共同的方法抽取出来, 以后让这些类遵守协议即可。
5.协议中的两个关键字
有2个关键字可以控制方法是否要实现(默认是@required,在大多数情况下,用途在于程序员之间的交流)
- @required:这个方法必须要实现(若不实现,编译器会发出警告)
- @optional:这个方法不一定要实现
6.协议的局限性
- 协 议是用来声明一大堆的方法,不能声明成员变量,也不能写实现方法,而某个类遵守了这个协议就可以进行协议中声明的方法的实现,这样虽然能解决OC的单继承 问题,但是有类型的限制,不能够更好的做到程序的扩展性,那么在协议的基础上通过什么能够更具扩展性呢?答案是—— 代理。
7.协议的注意事项
- (1)协议只能声明方法, 不能声明属性
- (2)父类遵守了某个协议, 那么子类也会自动遵守这个协议
- (3)在OC中一个类可以遵守1个或多个协议
注意: OC中的类只能有一个父类, 也就是说OC只有单继承
- (4)OC中的协议又可以遵守其它协议, 只要一个协议遵守了其它协议, 那么这个协议中就会自动包含其它协议的声明。
8.协议的应用场景及注意点
(1)做类型限定
- 类型限定就是限定一个类必须遵守某个协议
- 数据类型<协议名称> 变量名
- @proterty(nonatomic , strong)dog< DogRunning > *dog;
(2)类型限定的注意点
- 1.类型限定是写在数据类型的右边的
- 2.虽然在接受某一个对象的时候, 对这个对象进行了类型限定(限定它必须实现某个协议),
- 3但是并不意味着这个对象就真正的实现了该方法. 所以每次在调用对象的协议方法时应该进行一次验证
if([self.dag respondsToSelect:@selector(run)]){ [self.dog run]; }
四、委托代理
1.什么是设计模式?
- 在 计算机编程语言中有32种设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代 码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样,在iOS 中常用的实际模式为:代理方法,MVC模式,MVVC模式,单例模式等。
2.代理设计模式
代理模式是iOS开发中非常重要的一种设计模式,在UI中用的最多,有以下两种情况:
- (1)当对象A发生了一些行为,想告知对象B(让对象B成为对象A的代理对象)对象B想监听对象A的一些行为(让对象B成为对象A的代理对象)
- (2)当对象A无法处理某些行为的时候,想让对象B帮忙处理(让对象B成为对象A的代理对象)
3.代理编写规范
- (1)一般代理属于谁,就写在谁的头文件中定义。
- (2)协议的名称一般以他得属性的那个类的类名开头,后面跟上Protocol或者Delegate。
- (3)协议中的方法名称一般以协议的名称Protocol之前的作为开头。
4.接受遵守协议的代理对象关键字
- id关键字接受遵守协议的代理
@property(nonatomic,weak)id< DogDelegate>dog;
5.实现代理的步骤
发出代理者:
- (1)声明一个类: @class 类名
- (2)定义代理协议
- (3)将代理作为属性,类型限定代理遵守协议
- (4)声明方法,方法里调用代理,调用代理实现的协议方法 接受代理者
- (5)带入代理
- (6)遵守代理
- (7)实现协议中的方法 发出的代理者
- (8)实现调用代理者的方法
判断代理是否实现了协议中的方法
如果代理实现了代理中的方法,就调用
if ([self.delegate respondsToSelector:@selector(eat:)]) { [self.delegate eat:self]; }
@class与#import的区别
两个点:
(1)作用上得区别:
- ·#import会包含引用类的所有信息(内容),包括引用类的变量和方法 .@class仅仅告诉编译器有这么一个类,具体这个类有什么信息,完不知
(2 )效率上得区别.
- 如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的文件有改动,后面引用到这个文件的所有类都需要重新编译一变,效率非常低