OC基本语法总结(面试)
1:C和OC对比
OC中主要开发在什么平台上的应用程序?- 答:可以使用OC开发Mac OS X平台和iOS平台的应用程序
- 答:OC中新增关键字大部分是以@开头
- Block类型
- 指针类型(Class, id类型)
- 空类型
- 特殊类型(SEL, nil)
- 答:继承性,封装性,多态性
- 答:import 的功能和 include一样, 是将右边的文件拷贝到当前import的位置.为了降低程序员的负担, 防止重复导入, 避免程序员去书写 头文件卫士, 那么OC给出来一个新的预处理指令import
- import优点: 会自动防止重复拷贝
- 答: NSLog会自动换行
- NSLog在输出内容时会附加一些系统信息
- NSLog和printf接收的参数不一样
- 答:Foundation.h我们称之为主头文件, 主头文件中又拷贝了该工具箱中所有工具的头文件, 我们只需要导入主头文件就可以使用该工具箱中所有的工具, 避免了每次使用都要导入一个对应的头文件
2:面向对象基什么是面向对象?
- 答:对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。
- 1>面向对象是相对面向过程而言
- 2>面向对象和面向过程都是一种思想
面向过程
- 强调的是功能行为
- 关注的是解决问题需要哪些步骤
面向对象
- 将功能封装进对象,强调具备了功能的对象
- 关注的是解决问题需要哪些对象
3:类与对象什么是对象?
- 答:对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。
- 答:具有相同特性(数据元素)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。
- 答: 类(Class) 一个类由3个部分构成
- 类的名称:类名
- 类的属性:一组包含数据的属性
- 类的方法:允许对属性中包含的数据进行操作的方法
- 答:定义类要分为两部分: 声明和实现
- @interface 类名 : 父类名(NSObject)
- {
- 定义实例变量(成员变量,属性)
- }
方法的声明
- -(view)action;
- @implementation 类名
- 方法的具体实现
- @end;
- 答:OC类声明中属性只能在写@interface和@end之间的{}中
- 答: 创建对象格式:
- 类名 *指针名 = [类名 new];
- 答:实例化对象调用类方法new
1.给对象分配存储空间
2.初始化类中的实力变量 3.返回对象内存地址 如何访问对象中的属性?- 答:使用指针访问
- 格式: 指针名->_属性名;
4:OC方法方法和函数的区别? 答:
- 1)OC中的行为和C语言中的函数一样, 都是用来保存一段特定功能的代码
- C语言中定义一个函数, 分为声明和实现, 声明写在.h中, 实现写在.c中
- OC中定义一个方法, 也分为声明和实现, 声明写在@interface中, 实现写在@implementation
- C语言的函数分为两种: 内部函数和外部函数
- OC中的方法也分为两种; 类方法和对象方法
- 答:
- 方法的声明格式:
- (返回值类型)方法名;
- 方法的实现格式:
- (返回值类型)方法名{
- }
- 调用方法格式:
- [对象名 方法名];
- 或者 [类名 方法名];
- 答:OC方法中的()有特殊的用途, OC方法中的()是用来扩住数据类型的
- 带一个参数
- 方法声明
- ·- (返回值类型)方法名:(参数类型)参数名;
- 方法实现
- - (返回值类型)方法名:(参数类型)参数名{
- }
- 方法声明
- - (返回值类型)方法名1:(参数类型)参数名1 方法名2:(参数类型)参数名2;
- - (返回值类型)方法名1:(参数类型)参数名1 and方法名2:(参数类型)参数名
- - (返回值类型)方法名1:(参数类型)参数名1 :(参数类型)参数名2;
- 方法实现
- - (返回值类型)方法名1:(参数类型)参数名1 方法名2:(参数类型)参数名2{
- }
5:OC类方法类方法和对象方法的区别? 答: 0). 对象方法以-开头
- 类方法以+开头
1). 对象方法必须用对象调用
- 类方法必须用类来调用
2).对象方法中可以直接访问属性(成员变量)
- 类方法中不可以直接访问属性(成员变量)
3). 类方法和对象方法可以进行相互调用(展示代码)
类方法的应用场景? 答:- 类方法一般用于定义工具方法
- 字符串查找
- 文件操作
- 数据库操作
- 答: 类的第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项目可能会有多个人开发,如果多个人同时修改一个文件,那么就很可能会产生冲突,比如这个增加一个方法,那个人把这方法删掉了。另外就是当把多个人写功能合并起来的时候,也非常困难,写到一个文件中,无法顺畅的进行团队合作
- 在工作中,通常把不同的类放到不同的文件中,每个类的声明和实现分开
- 声明写在.h头文件中,
- 实现写在相应的.m文件中去,
- 类名是什么,文件名就是什么
- 显著提高团队协作的效率
- 提高程序的开发速度
- 提高程序的可维护性
- 提高代码的可读性
8:面向对象特性:面向对象三大特性有哪些?
- 答:继承性,封装性,多态性
- 封装: 屏蔽内部实现的细节, 仅仅对外提供共有的方法/接口
- 好处: 保证数据的安全性,将变化隔离
- 规范: 一般情况下不会对外直接暴露成员变量, 都会提供一些共有的方法进行赋值
- 成员变量都需要封装起来
- 答:一个类把自己的成员变量暴露给外部的时候,那么该类就失去对该成员变量的管理权,别人可以任意的修改你的成员变量。
9:geeter-setter方法什么是setter方法?sett方法的书写格式?
- 答:setter方法就是给成员变量赋值的方法
格式:
- (1) setter方法一定是对象方法
- (2) 一定没有返回值
- (3) 一定以set开头, 并且set后面跟上需要设置的成员变量的名称去掉下划线, 并且首字母大写
- (4) 一定有参数, 参数类型一定和需要设置的成员变量的类型一致, 并且参数名称就是成员变量的名称去掉下划线
- (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在哪一个方法中 , 如果在类方法那么就代表当前类, 如果在对象方法那么就代表”当前调用该方法的对象”
- (1)self会自动区分类方法和对象方法, 如果在类方法中使用self调用对象方法, 那么会直接报错
- (2)不能在对象方法或者类方法中利用self调用当前self所在的方法
- 可以用于在对象方法之间进行相互调用
- 可以用于在类方法之间进行相互调用
- 可以用于区分成员变量和局部变量同名的情况
11:继承基本概念什么是继承?什么是父类?什么是子类?如何实现继承? 答:
- 1)子类获得父类的特性就是继承
- 2)被继承的这个类我们称之为父类/ 超类
- 3)继承了某个类的类我们称之为子类
- 4)在声明子类的时候,在子类名称后面通过:父类名称方式来实现继承
@interface 子类名称 : 父类名称
@end类当B类继承A类, 那么B类就拥有A类所有的属性和方法(类方法/对象方法) 什么叫方法重写?重写后是以什么顺序调用方法的? 答:- (1)如果子类中有和父类中同名的方法, 那么我们称之为方法重写
- 注意: 继承中的方法调用顺序, 如果自己有就调用自己的, 如果自己没有就调用父类的
- “方法的调用顺序, 先自己再父类, 如果父类中没有再爷爷类, 如果爷爷类再没有就找爷爷的爸爸类
- 如果一直找到NSObject类都没有找到, 那么就会报错
- reason: ‘-[Iphone signalWithNumber:]: unrecognized selector sent to instance 0x1003043c0’
- 注意:在继承中方法可以重写, 但是属性(成员变量)不能重写
- 答:使用场景:当从父类继承的某个方法不适合子类,可以在子类中重写父类的这个方法。
- 不要以为继承可以提高代码的复用性, 以后但凡发现多个类当中有重复代码就抽取一个父类
- 只要满足一定的条件我们才能使用继承
- 条件: XXXX 是 XXX / 某某某 is a 某某某
- 提高代码的复用性
- 可以让类与类之间产生关系, 正是因为继承让类与类之间产生了关系所以才有了多态
- 耦合性太强(依赖性太强)
12:super关键字什么是super?
- super是个编译器的指令符号,只是告诉编译器在执行的时候,去调谁的方法.
- super在类方法中, 一定会调用父类的类方法
- super在对象方法中, 一定会调用父类的对象方法
- 可以利用super在任意方法中调用父类中的方法
- 子类重写父类的方法时想保留父类的一些行为
13:多态什么是多态?程序中是怎么体现多态的?
- 答:
- 多态就是某一类事物的多种形态
- 在程序中如何表现:
- 父类指针指向子类对象
- 1)有继承关系
- 2)子类重写父类方法
- 3)父类指针指向子类对象
- 答:提高了代码的扩展性,复用性
- 答:如果父类指针指向子类对象, 需要调用子类特有的方法, 必须先强制类型转换为子类才能调用
14:description方法使用%@了打印一个对象,输出的是什么内容?%@的原理是什么?
- 答:%@是用来打印对象的, description方法默认返回对象的描述信息(默认实现是返回类名和对象的内存地址).
- 其实%@的本质是用于打印字符串.
- 只要利用%@打印某个对象, 系统内部默认就会调用父类的description方法
- 调用该方法, 该方法会返回一个字符串, 字符串的默认格式 <类的名称: 对象的地址>
- 答:如果在description方法中利用%@输出self会造成死循环
- 建议: 在description方法中尽量不要使用self来获取成员变量
- 因为如果你经常在description方法中使用self, 可能已不小心就写成了 %@, self
15:私有变量和私有方法什么是私有变量?什么是私有方法? 答:
- 实例变量(成员变量)既可以在@interface中定义, 也可以在@implementation中定义
私有变量:
- 写在@implementation中的成员变量, 默认就是私有的成员变量, 并且和利用@private修饰的不太一样, 在@implementation中定义的成员变量在其它类中无法查看, 也无法访问
私有方法:
- 在@implementation中定义的私有变量只能在本类中访问
- 在Xocde4.4之前, 可以使用@porperty来代替getter/setter方法的声明
- 也就是说我们只需要写上@porperty就不用写getter/setter方法的声明
- 编译器只要看到@property,就知道我们要生成某一个属性的
getter/setter方法的声明
@propertyde格式?- 答:@property 数据类型 变量名;
- synthesize是一个编译器指令, 它可以简化我们getter/setter方法的实现
- (1)在@synthesize后面的age,告诉编译器, 需要实现哪个@property生成的声明
- (2)告诉@synthesize, 需要将传入的值赋值给谁和返回谁的值给调用者
如果在@synthesize后面没有告诉系统将传入的值赋值给谁, 系统默认会赋值给和@synthesize后面写得名称相同的成员变量
@synthesize age;property增强做了哪些事? 答:- (1)从Xcode4.4以后,对@property进行了增强, 以后只要利用一个@property就可以同时生成setter/getter方法的声明和实现
- (2)如果没有告诉@property要将传入的参数赋值给谁, 默认@property会将传入的属性赋值给_开头的成员变量
- 如果不想对传入的数据进行过滤, 仅仅是提供方法给外界操作成员变量, 那么就可以使用@property,并且系统会自动给我们生成一个_开头的成员变量
- 答:使用property增强,只会生成最简单的getter/setter方法的声明和实现, 并不会对传入的数据进行过滤
- 如果想对传入的数据进行过滤, 那么我们就必须重写getter/setter方法
- 如果重写了setter方法, 那么property就只会生成getter方法
- 如果重写了getter方法, 那么property就只会生成setter方法
- 如果同时重写了getter/setter方法, 那么property就不会自动帮我们生成私有的成员变量
16: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:静态数据类型和动态数据类型静态数据类型的特点:
- 在编译时就知道变量的类型,
- 知道变量中有哪些属性和方法
- 在编译的时候就可以访问这些属性和方法,
- 并且如果是通过静态数据类型定义变量, 如果访问了不属于静态数据类型的属性和方法, 那么编译器就会报错
- 在编译的时候编译器并不知道变量的真实类型, 只有在运行的时候才知道它的真实类型
- 并且如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错
- NSObject *是一个静态数据类型
- id 是一个动态数据类型
- 答:动态类型主要用在多态, 可以减少代码量, 避免调用子类特有的方法需要强制类型转换
- 答:由于动态数据类型可以调用任意方法, 所以有可能调用到不属于自己的方法, 而编译时又不会报错, 所以可能导致运行时的错误
- 答:为了避免动态数据类型引发的运行时的错误, 一般情况下如果使用动态数据类型保存一个对象, 在调用这个变量的方法之前会进行一次判断, 判断当前对象是否能够调用这个方法
18:构造方法什么是构造方法? 答:
- 在OC中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)no2 {3 if (self = [super initWithAge:age andName:name]) {4 _no = no;5 }6 return self;7 }
20:instancetype和id区别instancetype和id区别? 答: (1)id在编译的时候不能判断对象的真实类型
- instancetype在编译的时候可以判断对象的真实类型
- 如果是在以前, init的返回值是id, 那么将init返回的对象地址赋值给其它对象是不会报错的
- instancetype只能用于作为返回值
- 答:以后但凡自定义构造方法, 返回值尽量使用instancetype, 不要使用id
21:类工厂方法什么是类工厂方法?
- 答:用于快速创建对象的类方法, 我们称之为类工厂方法
- 答:类工厂方法中主要用于 给对象分配存储空间和初始化这块存储空间
- 1.一定是类方法 +
- 2.方法名称以类的名称开头, 首字母小写
- 3.一定有返回值, 返回值是id/instancetype
- 4.在类工厂方法实现中,调用本类的构造方法,创建实例对象,并返回实例对象
22:类的本质及存储细节类的本质是什么? 答:
- (1)类其实也是一个对象, 这个对象会在这个类第一次被使用的时候创建
- (2)只要有了类对象, 将来就可以通过类对象来创建实例对象
- (3)实例对象中有一个isa指针, 指向创建自己的类对象
- (4)类对象中保存了当前对象所有的对象方法
- (5)当给一个实例对象发送消息的时候, 会根据实例对象中的isa指针去对应的类对象中查找
- (6)所有类对象的继承关系就是元类对象的继承关系
23:类的启动过程
1.load方法
- “load方法调用时间:”
- 只要程序启动就会将所有类的代码加载到内存中, 放到代码区
- “调用次数”
- load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次
- “initialize方法调用时间:”
- 当当前类第一次被使用的时候就会调用(创建类对象的时候)
- “调用次数”
24:SEL类型SEL是什么类型? 答:
- SEL类型代表着方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系
- (1)SEL类型的第一个作用, 配合对象/类来检查对象/类中有没有实现某一个方法
- (2)SEL类型的第二个作用, 配合对象/类来调用某一个SEL方法
- (3)配合对象将SEL类型作为方法的形参
- 判断实例是否实现某个对象方法
- (BOOL)respondsToSelector: (SEL)selector
- 判断类是否实现某个类方法
- (BOOL)instancesRespondToSelector:(SEL)aSelector;
- - (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方法, [super dealloc]一定要写到所有代码的最后
- 如果你通过alloc、new、copy或mutableCopy来创建一个对象,那么你必须调用release或autorelease
- 只要你调用了retain,就必须调用一次release
- 有加就有减,曾经让对象的计数器+1,就必须在最后让对象的计数器-1
- (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 }
- readwrite: 既会生成getter也会生成setter, 默认什么都不写就是readwrite
- setter: 可以给生成的setter方法起一个名称
- assign: 不会帮我们生成set方法内存管理的代码, 仅仅只会生成普通的getter/setter方法, 默认什么都不写就是assign
- atomic :性能低(默认)
- nonatomic :性能高
- 在iOS开发中99.99%都是写nonatomic
- 1.相同类型的property修饰符不能同时使用
- 2.不同类型的property修饰符可以多个结合在一起使用, 多个之间用,号隔开
- 3.iOS开发中只要写上property, 那么就立刻写上nonatomic
- 答:
- 如果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了)。
- 1.完全消除了手动管理内存的烦琐, 让程序猿更加专注于app的业务
- 2.基本上能够避免内存泄露
- 3.有时还能更加快速,因为编译器还可以执行某些优化
- 默认所有指针变量都是强指针
- 被__strong修饰的指针
(2)弱指针
- 被__weak修饰的指针
- strong : 用于OC对象, 相当于MRC中的retain
- weak : 用于OC对象, 相当于MRC中的assign
- assign : 用于基本数据类型, 跟MRC中的assign一样
- (1)ARC下单对象内存管理
- (2)ARC下,所有的指针都是强指针
- (3)ARC, A对象想拥有B对象, 那么就需要用一个强指针指向B对象
- (4)A对象不用B对象了, 什么都不需要做, 编译器会自动帮我们做
- ARC和MRC一样, 如果A拥有B, B也拥有A, 那么必须一方使用弱指针
- 也就是说 一端用strong ,一端用weak
29:Category 分类书写Category的格式? 答:
// 分类的声明
- @interface ClassName (CategoryName)
- NewMethod; //在类别中添加方法
- //不允许在类别中添加变量
- @end
- ClassName: 需要给哪个类扩充方法
- CategoryName: 分类的名称
- NewMethod: 扩充的方法
- @implementation ClassName(CategoryName)
- NewMethod
- … …
- @end
- CategoryName: 分类的名称
- NewMethod: 扩充的方法
- (1)在不改变原来的类内容的基础上,为类增加一些方法。
- (2)一个庞大的类可以分模块开发,由多个人来编写,更有利于团队合作
- 答:先调用分类中的方法(最后参与编译的分类优先),再调用原来类中的方法,最后掉用父类中的方法
30:Extension 匿名扩展什么是类扩展?
- 答:延展类别又称为扩展(Extension),Extension是Category的一个特例
- 类扩展书写格式
- @interface 类名 ()
- @end
- 答:写在.m文件中,可以为某个类扩充一些私有的成员变量和方法
31:Block什么是Block?
- 答:Block是iOS中一种比较特殊的数据类型,用来保存某一段代码
- 答:Block用来保存某一段代码, 可以在恰当的时间再取出来调用
功能类似于函数和方法
- Block的格式?
- 答:Block的格式:
- 返回值类型 (^block变量名)(形参列表) = ^(形参列表) {
- };
32:协议什么是协议?
- 答:其他语言有接口的概念,接口就是一堆方法的声明没有实现.
- OC中没有接口的概念,OC中的接口就是协议.
- 协议Protocol是由一系列的方法声明组成的
- 格式:
- @protocol 协议名称
- // 方法声明列表
- @end
- 答:类遵守协议格式:
- @interface 类名 : 父类 <协议名称1, 协议名称2,…>
- @end
- (1)一个类可以遵守1个或多个协议
- (2)任何类只要遵守了Protocol,就相当于拥有了Protocol的所有方法声明
- (1)继承之后默认就有实现, 而protocol只有声明没有实现
- (2)相同类型的类可以使用继承, 但是不同类型的类只能使用protocol
- (3)protocol可以用于存储方法的声明, 可以将多个类中共同的方法抽取出来, 以后让这些类遵守协议即可
- 答:基协议:是基协议,是最根本最基本的协议,其中声明了很多最基本的方法。
- 注意:建议每个新的协议都要遵守NSObject协议
6.协议有哪些注意事项?
答:- (1)协议只能声明方法, 不能声明属性
- (2)父类遵守了某个协议, 那么子类也会自动遵守这个协议
- (3)在OC中一个类可以遵守1个或多个协议
- 注意: OC中的类只能有一个父类, 也就是说OC只有单继承
- (4)OC中的协议又可以遵守其它协议, 只要一个协议遵守了其它协议, 那么这个协议中就会自动包含其它协议的声明
- (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)一般情况下协议中的方法会将触发该协议的对象传递出去
- 一般情况下在.h中用@protocol 协议名称;告诉当前类 这是一个协议.
- 在.m中用#import真正的导入一个协议的声明
35:Foundation什么是框架? 答:
- 众多功能\API的集合.
- 框架是由许多类、方法、函数、文档按照一定的逻辑组织起来的集合,以便使研发程序变得更容易,在OS X下的Mac操作系统中大约有80个框架为所有程序开发奠定基础的框架称为Foundation 框架
- 1.Foundation框架是Mac\iOS中其他框架的基础
- 2.Foundation框架包含了很多开发中常用的数据类型:结构体,枚举, 类
- 答:一个NSString对象就代表一个字符串(文字内容)
- 一般称NSString为字符串类
- 如果通过@”“创建字符串, 那么会将字符串放到常量区中
- 如果是字符串常量, 那么只要内容相同 , 不会重复创建
- NSString *str1 = @"james";
- 如果是通过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);
- (1)URL的全称是Uniform Resource Locator(统一资源定位符)
- (2)URL是互联网上标准资源的地址
- (3)互联网上的每个资源都有一个唯一的URL,它包含的信息指出资源的位置
- (4)根据一个URL就能找到唯一的一个资源
- 答: URL = 协议头://主机地址/路径
- NSURL *url = [NSURL URLWithString:@"file:///Users/james/Desktop/str.txt"];
- NSURL *url = [[NSURL alloc] initWithString:@"file:///Users/james/Desktop/str.txt"];
- NSURL *url = [NSURL fileURLWithPath:@"/Users/james/Desktop/str.txt"];
- NSString *path = @"file://192.168.13.10/Users/james/Desktop/note/ja.txt";
- NSLog(@"url编码前: %@", path);
- path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSLog(@"url编码后: %@", path);
- NSString *path = @”file:///Users/james/Desktop/note/ja.txt”;
- NSLog(@”url编码前: %@”, path);
- path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSLog(@”url编码后: %@”, path);
- NSURL *url = [NSURL URLWithString:path];
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: 方法支持中文,并且省略协议头,但是只能获取本地方法
- 获取网络路径的信息–URLWithString
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];NSString *str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];NSLog(@"str = %@", str);
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 }
- NSUInteger location = [str rangeOfString:@”>”].location + 1;
(2)动态获取截取的长度
1 NSUInteger length = [str rangeOfString:@"
- 需求: 将&符号替换为/
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);
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);
- 本质就是在字符串的末尾加上一个/ 和指定的内容
- 注意: 如果路径后面已经有了/, 那么就不会添加了
- 如果路径后面有多个/, 那么会自动删除多余的/, 只保留一个
1 NSString *newStr = [str stringByAppendingPathComponent:@"james"]; 2 NSLog(@"%@", newStr);10.如何获取路径中文件的扩展名? 答:
- 本质就是从字符串的末尾开始查找., 截取第一个.后面的内容
NSString *newStr = [str pathExtension];NSLog(@"%@", newStr);
- 本质就是从字符串的末尾开始查找.,删除第一个.和.后面的内容
NSString *newStr = [str stringByDeletingPathExtension];NSLog(@"%@", newStr);
NSString *newStr = [str stringByAppendingPathExtension:@"jpg"];NSLog(@"%@", newStr);
NSString *newStr = [str uppercaseString];NSLog(@"%@", newStr);
14.如何将字符串转换为小写?
答:NSString *newStr2 = [newStr lowercaseString];NSLog(@"%@", newStr2);
15.如何将字符串的首字符转换为大写
答:NSString *newStr = [str capitalizedString];NSLog(@"%@", newStr);NSMutalbleString
- (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);
- insertString : 需要插入的字符串
- atIndex: 从哪里开始插入
NSRange range = [strM rangeOfString:@"xx"];[strM insertString:@"love" atIndex:range.location];NSLog(@"strM = %@", strM);
37:NSArray什么是NSArray?
- 答:NSArray是OC中的数组类,开发中建议尽量使用NSArray替代C语言中的数组
- (1)只能存放任意OC对象, 并且是有顺序的
- (2)不能存储非OC对象, 比如int\float\double\char\enum\struct等
- (3)它是不可变的,一旦初始化完毕后,它里面的内容就永远是固定的, 不能删除里面的元素, 也不能再往里面添加元素
- (4)NSArray使用NSLog()打印,输出的是小括号的格式。
- (5)NSArray中不能存储nil,因为NSArray认为nil是数组的结束(nil是数组元素结束的标记)。nil就是0。0也是基本数据类型,不能存放到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);
- }
- 每取出一个元素就会调用一次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);
NSArray *arr = @[@"eee", @"aaa", @"jjj"];BOOL flag = [arr writeToFile:@"/Users/james/Desktop/abc.plist" atomically:YES];NSLog(@"flag = %i", flag);
- 如果将一个数组写入到文件中之后, 本质是写入了一个XML文件
- 在iOS开发中一般情况下我们会将XML文件的扩展名保存为plist
- writeToFile只能写入数组中保存的元素都是Foundation框架中的类创建的对象, 如果保存的是自定义对象那么不能写入
- 从文件中读取一个数组,此方法在字典转模型中,经常用到
NSArray *newArray = [NSArray arrayWithContentsOfFile:@"/Users/james/Desktop/abc.plist"];NSLog(@"%@", newArray);
38:NSMutableArray
1.什么是可变数组?和NSArray有什么区别? 答:- (1)NSMutableArray是NSArray的子类
- (2)NSArray是不可变的,一旦初始化完毕后,它里面的内容就永远是固定的, 不能删除里面的元素, 也不能再往里面添加元素
- (3)NSMutableArray是可变的,随时可以往里面添加\更改\删除元素
- NSMutableArray *arrM = [NSMutableArray array];
- NSMutableArray *arrM = [[NSMutableArray alloc] init];
- 不能通过@[]来创建一个可变数组, 因为@[]创建出来的是一个不可变的数组
- [arrM addObject:@"james"];
- 将指定数组中的元素都取出来, 放到arrM中 \
- 并不是将整个数组作为一个元素添加到arrM中
[arrM addObjectsFromArray:@[@"james", @"jjj"]];
注意:- 以下是将整个数组作为一个元素添加
[arrM addObject:@[@"ss", @"jjj"]];NSLog(@"%@", arrM);
[arrM insertObject:@"xcq" atIndex:1];NSRange range = NSMakeRange(2, 2);NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:range];
- [arrM removeObjectAtIndex:0]
- [arrM removeLastObject];
- [arrM removeObject:@"A"];
- [arrM replaceObjectAtIndex:1 withObject:@"M"];
- arrM[0] = @"ZS";
- NSLog(@"%@", arrM);
- 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"]);
- NSDictionary *dict = @{@"name":@"james", @"age":@"22", @"height":@"1.75"};
- NSLog(@"count = %lu", [dict count]);
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);
- }];
NSDictionary *dict = @{@"name":@"james", @"age":@"22", @"height":@"1.75"};[dict writeToFile:@"/Users/james/Desktop/info.plist" atomically:YES];
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 setValuesForKeysWithDictionary:@{@"age":@"30", @"height":@"1.75"}];
- [dictM removeObjectForKey:@"name"];
- [dictM removeObjectsForKeys:@[@"age", @"height"]];
- [dictM setObject:@"88" forKey:@"age"];
- dictM[@"age"] = @"88";
- NSMutableDictionary *dictM = @{@”name”:@”james”};//编译就会报错
- [dictM setObject:@”30” forKey:@”age”];
- 如果是不可变字典, 那么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:NSNumber1.NSNumber的应用场景? 答:
- NSNumber可以将基本数据类型包装成对象,这样就可以间接将基本数据类型存进NSArray\NSDictionary中
- 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);
- int temp = [ageN intValue];
- double temp = [numberN doubleValue];
NSNumber *temp = @(number);NSNumber *temp =@10.10;NSLog(@"%@", temp);NSValue
1.NSValue的应用场景? 答:
- (1)NSNumber是NSValue的子类, 但NSNumber只能包装数字类型
- (2)NSValue可以包装任意值
- 因此, 可以用NSValue将结构体包装后,加入NSArray\NSDictionary中
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:NSDate1.NSDate的应用场景?
- 答:NSDate可以用来表示时间, 可以进行一些常见的日期\时间处理
- [NSDate date]返回的就是当前0时区的时间
- NSDate *now = [NSDate date];
- NSTimeZone *zone = [NSTimeZone systemTimeZone];
- NSInteger seconds = [zone secondsFromGMTForDate:now];
- // NSLog(@"seconds = %lu", seconds);
- NSDate *newDate = [now dateByAddingTimeInterval:seconds];
- NSLog(@"newDate = %@", newDate);
- NSDate *now = [NSDate date];
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
- formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
- NSString *str = [formatter stringFromDate:now];
- NSLog(@"%@", str);
- NSString *str = @"2015-08-20 07:05:26 +0000";
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
- 如果是从NSString格式化为NSDate, 那么dateFormat的格式, 必须和字符串中的时间格式一致, 否则可能转换失败
(3)指定格式
- formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
- NSDate *date = [formatter dateFromString:str];
- NSLog(@"%@", date);
43:NSCalendar1.如何获取当前时间的年月日时分秒? 答: (1)获取当前时间
- NSDate *now = [NSDate date];
- NSLog(@"now = %@", now);
- 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);
- 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);
- 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:NSFileManager1.什么是NSFileManager?如何获取NSFileManager 对象? 答:
- NSFileManager是用来管理文件系统的
- 它可以用来进行常见的文件\文件夹操作
- NSFileManager *manager = [NSFileManager defaultManager];
BOOL flag = [manager fileExistsAtPath:@"/Users/james/Desktop/video/01-NSArray.text"];NSLog(@"flag = %i", flag);
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);
- createDirectoryAtPath: 告诉系统文件夹需要创建到什么位置
- withIntermediateDirectories: 如果指定的文件中有一些文件夹不存在, 是否自动创建不存在的文件夹
- attributes: 指定创建出来的文件夹的属性
- error: 是否创建成功, 如果失败会给传入的参数赋值
- 注意: 该方法只能用于创建文件夹, 不能用于创建文件
BOOL flag = [manager createDirectoryAtPath:@"/Users/james/Desktop/abc/llq" withIntermediateDirectories:YES attributes:nil error:nil];NSLog(@"%i", flag);
- createFileAtPath: 指定文件创建出来的位置
- contents : 文件中的内容
- attributes: 创建出来的文件的属性
- NSData : 二进制数据
注意: 该方法只能用于创建文件, 不能用于创建文件夹
45:Copy1.使用copy功能的前提是什么? 答:
- copy : 需要遵守NSCopying协议,实现copyWithZone:方法
- 使用mutableCopy的前提
- 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法
- 一个对象可以调用copy或mutableCopy方法来创建一个副本对象
- copy : 创建的是不可变副本(如NSString、NSArray、NSDictionary)
- mutableCopy :创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
- 修改源对象的属性和行为,不会影响副本对象
- 修改副本对象的属性和行为,不会影响源对象
- 因为原来的对象是不能修改的, 拷贝出来的对象也是不能修改的
- 既然两个都不能修改, 所以永远不能影响到另外一个对象, 那么已经符合需求
- 所以: OC为了对内存进行优化, 就不会生成一个新的对象
46:copy内存管理1.浅复制(浅拷贝,指针拷贝,shallow copy)
- 源对象和副本对象是同一个对象
- 源对象(副本对象)引用计数器+1,相当于做一次retain操作
- 本质是:没有产生新的对象
- 源对象和副本对象是不同的两个对象
- 源对象引用计数器不变,副本对象计数器为1(因为是新产生的)
- 本质是:产生了新的对象
47:copy和Property1.@property中的copy的作用是什么? 答:
- (1)防止外界修改内部的数据
- (2)可以使用copy保存block, 这样可以保住block中使用的外界对象的命
- block默认存储在栈中, 栈中的block访问到了外界的对象, 不会对对象进行retain
- 1> copy : 只用于NSString\block
- 2> retain : 除NSString\block以外的OC对象
- 3> assign :基本数据类型、枚举、结构体(非OC对象),当2个对象相互引用,一端用retain,一端用assign
- 1> copy : 只用于NSString\block
- 2> strong : 除NSString\block以外的OC对象
- 3> weak : 当2个对象相互引用,一端用strong,一端用weak
- 4> assgin : 基本数据类型、枚举、结构体(非OC对象)
1 __block Person *p = [[Person alloc] init]; // 12 p.name = @"james";3 NSLog(@"retainCount = %lu", [p retainCount]);4 p.pBlock = ^{5 NSLog(@"name = %@", p.name); // 26 };7 NSLog(@"retainCount = %lu", [p retainCount]);8 p.pBlock();
48:自定义类实现Copy1.自定义类如何实现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 *)zone12 {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.什么是单例模式?
- 答:类的对象成为系统中唯一的实例,提供一个访问点,供客户类 共享资源
- 单例就是无论怎么创建都只能有一个实例对象
- (1)类只能有一个实例,而且必须从一个为人熟知的访问点对其进行访问,比如工厂方法。
- (2)这个唯一的实例只能通过子类化进行扩展,而且扩展的对象不会破坏客户端代码。
- (1)一般情况下创建一个单利对象都有一个与之对应的类方法
- (2)一般情况下用于创建单利对象的方法名称都以share开头, 或者以default开头
单例在多线程的应用?
答:多线程中的单例1 + (instancetype)allocWithZone:(struct _NSZone *)zone2 {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
- 不允许在声明类别的时候定义变量
- (1)新方法的实现必须写在@ implementation与@end之间
- (2)ClassName 现有类的类名
- (3)CategoryName 待声明的类别名称
- (4)NewMethod 新添加的方法的实现
- 1. 分类只能增加方法, 不能增加成员变量
- 2. 分类中写property只会生成方法声明
- 3. 分类可以访问原来类中的成员变量
- 4. 如果分类和原来类出现同名的方法, 优先调用分类中的方法, 原来类中的方法会被忽略
- 方法调用顺序(从高到低) 分类 –>原来类 –>父类
- 分类应用场景:开发中经常使用的方法,可以抽出一个分类,团队开发中,多人共同开发,可以写多个分类,最后进行融合。
二、Exetention 匿名扩展1.匿名扩展定义
- 延展类别又称为扩展(Extendsion),Extension是Category的一个特例
- 可以为某个类扩充一些私有的成员变量和方法写在.m文件中
- 英文名是Class Extension
- @interface 类名 ()
- {
- int _age;
- }
- - (void)say;
- @end
三、Block1.Block简介 Block是iOS中一种比较特殊的数据类型 Block是苹果官方特别推荐使用的数据类型, 应用场景比较广泛。 Block应用场景:
- 动画
- 多线程
- 集合遍历
- 网络请求回调
- 用来保存某一段代码, 可以在恰当的时间再取出来调用
- 功能有点类似于函数和方法。
- 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是完全不同的。
- 用来声明一些方法,也就说, 一个Protocol是由一系列的方法声明组成的。
- @protocol 协议名称
- // 方法声明列表
- @end
(2)类遵守协议
- 一个类可以遵守1个或多个协议
- 任何类只要遵守了Protocol,就相当于拥有了Protocol的所有方法声明.
- 继承之后默认就有实现, 而protocol只有声明没有实现
- 相同类型的类可以使用继承, 但是不同类型的类只能使用protocol
- protocol可以用于存储方法的声明, 可以将多个类中共同的方法抽取出来, 以后让这些类遵守协议即可。
有2个关键字可以控制方法是否要实现(默认是@required,在大多数情况下,用途在于程序员之间的交流)
- @required:这个方法必须要实现(若不实现,编译器会发出警告)
- @optional:这个方法不一定要实现
- 协 议是用来声明一大堆的方法,不能声明成员变量,也不能写实现方法,而某个类遵守了这个协议就可以进行协议中声明的方法的实现,这样虽然能解决OC的单继承 问题,但是有类型的限制,不能够更好的做到程序的扩展性,那么在协议的基础上通过什么能够更具扩展性呢?答案是—— 代理。
- (1)协议只能声明方法, 不能声明属性
- (2)父类遵守了某个协议, 那么子类也会自动遵守这个协议
- (3)在OC中一个类可以遵守1个或多个协议
注意: OC中的类只能有一个父类, 也就是说OC只有单继承
- (4)OC中的协议又可以遵守其它协议, 只要一个协议遵守了其它协议, 那么这个协议中就会自动包含其它协议的声明。
- 类型限定就是限定一个类必须遵守某个协议
- 数据类型<协议名称> 变量名
- @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模式,单例模式等。
- (1)当对象A发生了一些行为,想告知对象B(让对象B成为对象A的代理对象)对象B想监听对象A的一些行为(让对象B成为对象A的代理对象)
- (2)当对象A无法处理某些行为的时候,想让对象B帮忙处理(让对象B成为对象A的代理对象)
- (1)一般代理属于谁,就写在谁的头文件中定义。
- (2)协议的名称一般以他得属性的那个类的类名开头,后面跟上Protocol或者Delegate。
- (3)协议中的方法名称一般以协议的名称Protocol之前的作为开头。
- id关键字接受遵守协议的代理
- (1)声明一个类: @class 类名
- (2)定义代理协议
- (3)将代理作为属性,类型限定代理遵守协议
- (4)声明方法,方法里调用代理,调用代理实现的协议方法 接受代理者
- (5)带入代理
- (6)遵守代理
- (7)实现协议中的方法 发出的代理者
- (8)实现调用代理者的方法
判断代理是否实现了协议中的方法
如果代理实现了代理中的方法,就调用if ([self.delegate respondsToSelector:@selector(eat:)]) { [self.delegate eat:self]; }
- ·#import会包含引用类的所有信息(内容),包括引用类的变量和方法 .@class仅仅告诉编译器有这么一个类,具体这个类有什么信息,完不知
(2 )效率上得区别.
-
- 如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的文件有改动,后面引用到这个文件的所有类都需要重新编译一变,效率非常低