category 和 extension 的区别
- 分类有名字,类扩展没有分类名字,是一种特殊的分类
- 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展能够扩展属性、成员变量和方法
define 和 const常量有什么区别?
- define在预处理阶段进行替换,const常量在编译阶段使用
- 宏不作类型检查,仅仅进行替换,const常量有数据类型,会执行类型检查
- define不能调试,const常量能够调试
- define定义的常量在替换后运行过程当中会不断地占用内存,而const定义的常量存储在数据段只有一份copy,效率更高
- define能够定义一些简单的函数,const不能够
block和weak修饰符的区别?
- __block无论是ARC仍是MRC模式下均可以使用,能够修饰对象,也能够修饰基本数据类型
- __weak只能在ARC模式下使用,只能修饰对象(NSString),不能修饰基本数据类型
- block修饰的对象能够在block中被从新赋值,weak修饰的对象不能够
static关键字的做用
- 函数(方法)体内 static 变量的做用范围为该函数体,该变量的内存只被分配一次,所以其值在下次调用时仍维持上次的值;
- 在模块内的 static 全局变量能够被模块内所用函数访问,但不能被模块外其它函数访问;
- 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明 它的模块内;
- 在类中的 static 成员变量属于整个类所拥有,对类的全部对象只有一份拷贝;
- 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,于是只能访问类的static 成员变量
堆和栈的区别
- 从管理方式来说
- 对于栈来说,是由编译器自动管理,无需咱们手工控制;
- 对于堆来讲,释放工做由程序员控制,容易产生内存泄露(memory leak)
- 从申请大小大小方面讲
- 从数据存储方面来说
- 栈空间中通常存储基本类型,对象的地址
- 堆空间通常存放对象自己,block的copy等
风格纠错题

typedef NS_ENUM(NSInteger, CYLSex)
{
CYLSexMan,
CYLSexWoman
};
@interface CYLUser : NSObject
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readwrite) CYLSex sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
@end
Objective-C使用什么机制管理对象内存?
- MRC 手动引用计数
- ARC 自动引用计数,如今一般ARC
- 经过 retainCount 的机制来决定对象是否须要释放。 每次 runloop 的时候,都会检查对象的 retainCount,若是retainCount 为 0,说明该对象没有地方须要继续使用了,能够释放掉了
ARC经过什么方式帮助开发者管理内存?
ARC是为了解决什么问题诞生的?
- 首先解释ARC: automatic reference counting自动引用计数
- 了解MRC的缺点
- 在MRC时代当咱们要释放一个堆内存时,首先要肯定指向这个堆空间的指针都被release了
- 释放指针指向的堆空间,首先要肯定哪些指针指向同一个堆,这些指针只能释放一次(MRC下即谁建立,谁释放,避免重复释放)
- 模块化操做时,对象可能被多个模块建立和使用,不能肯定最后由谁去释放
- 多线程操做时,不肯定哪一个线程最后使用完毕
- 综上所述,MRC有诸多缺点,很容易形成内存泄露和坏内存的问题,这时苹果为尽可能解决这个问题,从而诞生了ARC
ARC下还会存在内存泄露吗?
- 循环引用会致使内存泄露
- Objective-C对象与CoreFoundation对象进行桥接的时候若是管理不当也会形成内存泄露
- CoreFoundation中的对象不受ARC管理,须要开发者手动释放
什么状况使用weak关键字,相比assign有什么不一样?
- 首先明白什么状况使用weak关键字?
- 在ARC中,在有可能出现循环引用的时候,每每要经过让其中一端使用weak来解决,好比:delegate代理属性,代理属性也可以使用assign
- 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性通常也使用weak;固然,也可使用strong,可是建议使用weak
- weak 和 assign的不一样点
- weak策略在属性所指的对象遭到摧毁时,系统会将weak修饰的属性对象的指针指向nil,在OC给nil发消息是不会有什么问题的;若是使用assign策略在属性所指的对象遭到摧毁时,属性对象指针还指向原来的对象,因为对象已经被销毁,这时候就产生了野指针,若是这时候在给此对象发送消息,很容形成程序奔溃
- assigin 能够用于修饰非OC对象,而weak必须用于OC对象
@property 的本质是什么?
- @property其实就是在编译阶段由编译器自动帮咱们生成ivar成员变量,getter方法,setter方法
ivar、getter、setter是如何生成并添加到这个类中的?
-
使用“自动合成”( autosynthesis)程序员
-
这个过程由编译器在编译阶段执行自动合成,因此编辑器里看不到这些“合成方法”(synthesized method)的源代码面试
-
除了生成getter、setter方法以外,编译器还要自动向类中添加成员变量(在属性名前面加下划线,以此做为实例变量的名字)安全
-
为了搞清属性是怎么实现的,反编译相关的代码,他大体生成了五个东西多线程
// 该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远
OBJC_IVAR_$类名$属性名称
// 方法对应的实现函数
setter与getter
// 成员变量列表
ivar_list
// 方法列表
method_list
// 属性列表
prop_list
- 每次增长一个属性,系统都会在ivar_list中添加一个成员变量的描述
- 在method_list中增长setter与getter方法的描述
- 在prop_list中增长一个属性的描述
- 计算该属性在对象中的偏移量
- 而后给出setter与getter方法对应的实现,在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了可以读取正确字节数,系统对象偏移量的指针类型进行了类型强转
@protocol 和 category 中如何使用 @property
- 在protocol中使用property只会生成setter和getter方法声明,咱们使用属性的目的,是但愿遵照我协议的对象能实现该属性
- category 使用 @property也是只会生成setter和getter方法声明,若是咱们真的须要给category增长属性的实现,须要借助于运行时的两个函数
objc_setAssociatedObject
objc_getAssociatedObject
@property后面能够有哪些修饰符?
- 原子性—nonatomic特质
- 若是不写默认状况为atomic(系统会自动加上同步锁,影响性能)
- 在iOS开发中尽可能指定为nonatomic,这样有助于提升程序的性能
- 读/写权限—readwrite(读写)、readooly (只读)
- 内存管理语义—assign、strong、 weak、unsafe_unretained、copy
- 方法名—getter=、setter=
@property (nonatomic, getter=isOn) BOOL on;
// setter=这种不经常使用,也**不推荐**使用。故不在这里给出写法
- 不经常使用的:nonnull,null_resettable,nullable
使用atomic必定是线程安全的吗?
- 不是,atomic的本意是指属性的存取方法是线程安全的,并不保证整个对象是线程安全的。
- 举例:声明一个NSMutableArray的原子属性stuff,此时self.stuff 和self.stuff = othersulf都是线程安全的。可是,使用[self.stuff objectAtIndex:index]就不是线程安全的,须要用互斥锁来保证线程安全性
@synthesize 和 @dynamic分别有什么做用
- @property有两个对应的词,一个是@synthesize,一个是@dynamic。若是@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
- @synthesize的语义是若是你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法
- @dynamic告诉编译器:属性的setter与getter方法由用户本身实现,不自动生成(固然对于readonly的属性只需提供getter便可)
- 假如一个属性被声明为@dynamic var,而后你没有提供@setter方法和@getter方法,编译的时候没问题,可是当程序运行到instance.var = someVar,因为缺setter方法会致使程序崩溃;或者当运行到 someVar = instance.var时,因为缺getter方法一样会致使崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定
ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
- 基本数据:atomic,readwrite,assign
- 普通的OC对象:atomic,readwrite,strong
@synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?
在有了自动合成属性实例变量以后,@synthesize还有哪些使用场景?
- 首先的搞清楚什么状况下不会autosynthesis(自动合成)
- 同时重写了setter和getter时
- 重写了只读属性的getter时
- 使用了@dynamic时
- 在 @protocol 中定义的全部属性
- 在 category 中定义的全部属性
- 重载的属性,当你在子类中重载了父类中的属性,必须 使用@synthesize来手动合成ivar
- 应用场景
- 当你同时重写了setter和getter时,系统就不会生成ivar)。这时候有两种选择
- 手动建立ivar
- 使用@synthesize foo = _foo;,关联@property与ivar
- 能够用来修改为员变量名,通常不建议这么作,建议使用系统自动生成的成员变量
怎么用 copy 关键字?
- NSString、NSArray、NSDictionary等等常用copy关键字,是由于他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,为确保对象中的属性值不会无心间变更,应该在设置新属性值时拷贝一份,保护其封装性
- block也常用copy关键字
- block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 能够把它放到堆区.
- 在ARC中写不写都行:对于 block 使用 copy 仍是 strong 效果是同样的,可是建议写上copy,由于这样显示告知调用者“编译器会自动对 block 进行了 copy 操做”
用@property声明的NSString(或NSArray,NSDictionary)常用copy关键字,为何?若是改用strong关键字,可能形成什么问题?
- 由于父类指针能够指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy不管给我传入是一个可变对象仍是不可对象,我自己持有的就是一个不可变的副本.
- 若是咱们使用是strong,那么这个属性就有可能指向一个可变对象,若是这个可变对象在外部被修改了,那么会影响该属性.
复制详解
-
浅复制(shallow copy):在浅复制操做时,对于被复制对象的每一层都是指针复制。
-
深复制(one-level-deep copy):在深复制操做时,对于被复制对象,至少有一层是深复制。
-
彻底复制(real-deep copy):在彻底复制操做时,对于被复制对象的每一层都是对象复制。
-
非集合类对象的copy与mutableCopy
[不可变对象 copy] // 浅复制
[不可变对象 mutableCopy] //深复制
[可变对象 copy] //深复制
[可变对象 mutableCopy] //深复制
-
集合类对象的copy与mutableCopy
[不可变对象 copy] // 浅复制
[不可变对象 mutableCopy] //单层深复制
[可变对象 copy] //单层深复制
[可变对象 mutableCopy] //单层深复制
-
这里须要注意的是集合对象的内容复制仅限于对象自己,对象元素仍然是指针复制
这个写法会出什么问题: @property (copy) NSMutableArray *array;
- 由于copy策略拷贝出来的是一个不可变对象,然而却把它当成可变对象使用,很容易形成程序奔溃
- 这里还有一个问题,该属性使用了同步锁,会在建立时生成一些额外的代码用于帮助编写多线程程序,这会带来性能问题,经过声明nonatomic能够节省这些虽然很小可是没必要要额外开销,在iOS开发中应该使用nonatomic替代atomic
如何让自定义类能够用 copy 修饰符?如何重写带 copy 关键字的 setter?
- 若想令本身所写的对象具备拷贝功能,则需实现NSCopying协议。若是自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopyiog与NSMutableCopying协议,不过通常没什么必要,实现NSCopying协议就够了
// 实现不可变版本拷贝
- (id)copyWithZone:(NSZone *)zone;
// 实现可变版本拷贝
- (id)mutableCopyWithZone:(NSZone *)zone;
// 重写带 copy 关键字的 setter
- (void)setName:(NSString *)name
{
_name = [name copy];
}
+(void)load; +(void)initialize;有什么用处?
- +(void)load;
- 当类对象被引入项目时, runtime 会向每个类对象发送 load 消息
- load 方法会在每个类甚至分类被引入时仅调用一次,调用的顺序:父类优先于子类, 子类优先于分类
- 因为 load 方法会在类被import 时调用一次,而这时每每是改变类的行为的最佳时机,在这里可使用例如method swizlling 来修改原有的方法
- load 方法不会被类自动继承
- +(void)initialize;
- 也是在第一次使用这个类的时候会调用这个方法,也就是说 initialize也是懒加载
- 总结:
- 在Objective-C中,runtime会自动调用每一个类的这两个方法
- +load会在类初始加载时调用
- +initialize会在第一次调用类的类方法或实例方法以前被调用
- 这两个方法是可选的,且只有在实现了它们时才会被调用
- 二者的共同点:两个方法都只会被调用一次
Foundation对象与Core Foundation对象有什么区别
addObserver:forKeyPath:options:context:各个参数的做用分别是什么,observer中须要实现哪一个方法才能得到KVO回调?
/**
1\. self.person:要监听的对象
2\. 参数说明
1> 观察者,负责处理监听事件的对象
2> 要监听的属性
3> 观察的选项(观察新、旧值,也能够都观察)
4> 上下文,用于传递数据,能够利用上下文区分不一样的监听
*/
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
/**
* 当监控的某个属性的值改变了就会调用
*
* @param keyPath 监听的属性名
* @param object 属性所属的对象
* @param change 属性的修改状况(属性原来的值、属性最新的值)
* @param context 传递的上下文数据,与监听的时候传递的一致,能够利用上下文区分不一样的监听
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@对象的%@属性改变了:%@", object, keyPath, change);
}
KVO内部实现原理
- KVO是基于runtime机制实现的
- 当某个类的属性对象第一次被观察时,系统就会在运行期动态地建立该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
- 若是原类为Person,那么生成的派生类名为NSKVONotifying_Person
- 每一个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
- 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变以前, willChangeValueForKey: 必定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
- 补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让咱们误认为仍是使用的当前类,从而达到隐藏生成的派生类
如何手动触发一个value的KVO
- 自动触发的场景:在注册KVO以前设置一个初始值,注册以后,设置一个不同的值,就能够触发了
- 想知道如何手动触发,必须知道自动触发 KVO 的原理,见上面的描述
- 手动触发演示
@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad
{
[super viewDidLoad];
// “手动触发self.now的KVO”,必写。
[self willChangeValueForKey:@"now"];
// “手动触发self.now的KVO”,必写。
[self didChangeValueForKey:@"now"];
}
若一个类有实例变量NSString *_foo,调用setValue:forKey:时,是以foo仍是_foo做为key?
KVC的keyPath中的集合运算符如何使用?
- 必须用在集合对象上或普通对象的集合属性上
- 简单集合运算符有@avg, @count , @max , @min ,@sum
KVC和KVO的keyPath必定是属性么?
文末推荐:iOS热门文集&视频解析
-
① iOS底层技术
-
② iOS逆向防御
-
③ 大厂面试题+底层技术+逆向安防+Swift