Cocoa 和 Cocoa Touch是什么
-
Cocoa包含Foundation和AppKit框架,用于开发Mac OS X系统的应用程序
-
Cocoa Touch包含Foundation和UIKit框架,用于开发iOS系统的应用程序
-
Cocoa Touch底层技术架构主要分为4层
- 可触摸层Cocoa Touch:UI组件,触摸事件和事件驱动,系统接口
- 媒体层 Media:音视频播放,动画,2D和3D图形
- Core Service:核心服务层,底层特性,文件,网络,位置服务区等
- Core OS:内存管理,底层网络,硬盘管理
为什么说OC是一门动态语言
OC作为一门面向对象的语言,具有封装、继承、多态的语言特性,可以在运行时动态改变类的结构:添加新函数、删除旧函数等,所以说它是一门动态语言
动态类型
- 一个变量所指向的类型推迟到运行时才具体知道是什么类型,最直接的就是id类型。
- 静态类型是强类型,而动态类型属于弱类型,运行时决定接受者
动态绑定
- 将调用方法的确定也推迟到运行时。
- 在编译时,方法的调用并不和代码绑定在一起,只有在消息发送出来之后,才确定被调用的代码,需要传什么参数进去
动态加载
在运行期间加载需要的资源或可执行代码
对于语句NSString * obj = [[NSData alloc] init]
obj在编译时和运行时分别是什么类型?
编译时是NSString的类型;运行时是NSData类型的对象
对于OC来讲,它最大优缺点是什么?
优点
- OC是C语言的超集,在C语言基础上增加了面向对象特性,,开发使用起来方便高效
- 分类可以快速扩展类的方法,扩展模块之间相互不影响
- 运行时特性,动态特性提高了编程的灵活性
- OC可以与C / C++进行混编
缺点
- 不支持多继承,多继承可以使用分类,协议,消息转发来弥补
- 不支持运算符重载
- 使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到,如内联函数等,性能低劣
- 执行效率比C低,语法怪异
什么是懒加载
- 懒加载 也叫做
延迟加载
,它的核心思想是把对象的实例化尽量延迟,在需要使用的时候才会初始化。这样做的好处是可以减轻大量对象实例化对资源的消耗 - 另外懒加载把对象的实例化代码抽取、独立出来,提高了代码的可读性,以便更好的组织代码
什么是类工厂方法?
类工厂方法就是用来快速创建对象的类方法, 它可以直接返回一个初始化好的对象
- 一定是类方法
- 返回值需要是 id/instancetype 类型
为什么iOS 9 以后通知不再需要手动移除
- 在 MRC 时代,通知中心持有的是注册者的 unsafe_unretained 指针,在注册者被回收时若不对通知进行手动移除,则指针指向被回收的内存区域,变为野指针。此时发送通知会造成 crash 。
- 在 iOS 9 以后,通知中心持有的是注册者的 weak 指针,这时即使不对通知进行手动移除,指针也会在注册者被回收后自动置空
id、nil、Nil、NULL、NSNull的区别
- id 声明的对象具有运行时的特性,即可以指向任意类型的OC对象
- nil 是一个实例对象值,常用于清空一个对象和作为arrayWithObjects的结束符
- Nil 是一个类对象值,常用于清空一个类对象
- NULL 指向基本数据类型的空指针(C语言的变量的指针为空)
- NSNull 是一个对象,常用于需要占位的场合
self. 跟 self-> 区别?
- self. 是调用
getter或者setter
方法,self是一个指向当前对象的指针 - self-> 是直接访问成员变量
#include、#import、@class、#import<>、 #import“”的区别
- 在C语言中,使用
#include
来引入头文件,防止重复导入需要用#ifndef...#define...#endif
宏分割 - 在OC语言中,使用
#import
来引入头文件,不会引起交叉编译,可以防止重复引入和避免头文件递归引入 @class
用来告诉编译器有这样一个类,编译代码时不报错,也不会拷贝头文件,可以解决头文件的相互引用。仅在需要使用该类时使用#import
引入。- #import<> 用于引用系统头文件,会在系统头文件内寻找
- #import“” 用于引用本工程的头文件,它会先在工程中找,找不到再去引用系统同名头文件
id、instancetype的区别
id
可以作为方法的返回值以及参数类型,也可以用来定义变量。编译器不检查id的类型instancetype
只能作为方法的返回值- instancetype对比id的好处就是:能精确的限制返回值的具体类型
NSObject和id的区别
- NSObject 和 id都可以指向任何对象
- NSObject 对象会在编译时进行检查,需要强制类型转换
- id类型不会在编译时检查,不需要强制类型转换
isEqual、isEqualToString、==、hash区别
- == 比较两个对象的内存地址,若一样就返回TRUE,若不一样就返回FALSE
- isEqual:首先检查指针的等同性,然后是类的等同性,最后对对象的属性和变量检查,比较成功返回true
- isEqualToString:字符串比较,只比较字符串本身的内容是否一致,不比较内存地址
- hash 是一个类方法,任何类都可以调用这个方法,返回的结果是一个NSInteger值(如果两个对象相等,那么他们的hash值一定相等,但是,如果两个对象的哈希值相等是不能一定推出来这两个对象是相等的)
isMemberOfClass 和 isKindOfClass 联系与区别
- 联系:两者都能检测一个对象是否是某个类的成员
- 区别:
isKindOfClass
不仅用来确定一个对象是否是一个类的成员,也可以用来确定一个对象是否是派生自该类的成员 ,而isMemberOfClass
只能做到第一点
方法和选择器有何不同
- selector是一个方法的名字,method是一个组合体,包含了名字和实现。
- 通过一个selector可以找到方法地址,进而调用一个方法
new关键字作用是什么
-
向计算机(堆区)申请内存空间
-
初始化实例变量
-
返回所申请空间的首地址
alloc init
和new的区别
-
alloc的作用是分配内存给对象,使对象不被释放,将地址返回给指针
-
init 就是为分配好的内存进行初始化
-
new和alloc/init在功能上几乎是一致的,分配内存并完成初始化。
差别在于,采用new的方式只能采用默认的init方法完成初始化,采用alloc的方式可以用其他定制的初始化方法
OC中的NSInteger 和int 有什么区别
-
NSInteger实际上一个typedef
#if __LP64__ || 0 || NS_BUILD_32_LIKE_64 typedef long NSInteger; typedef unsigned long NSUInteger; #else typedef int NSInteger; typedef unsigned int NSUInteger; #endif
-
在32位操作系统上,NSInteger 等价于 int,即32位
-
在64位操作系统上,NSInteger 等价于 long,即64位
OC的数据类型和C的基本数据类型有什么区别?
- OC的数据类型有
NSString,NSNumber,NSArray,NSMutableArray,NSData
等等,这些都是class,创建后便是对象, - C语言的基本数据类型int,只是一定字节的内存空间,用于存放数值
类方法和实例方法有什么区别
-
类方法:
- 类方法是属于类对象的
- 类方法只能通过类对象调用
- 类方法中的self是类对象
- 类方法可以调用其他的类方法
- 类方法中不能访问成员变量
- 类方法中不能直接调用对象方法
-
实例方法:
- 实例方法是属于实例对象的
- 实例方法只能通过实例对象调用
- 实例方法中的self是实例对象
- 实例方法中可以访问成员变量
- 实例方法中直接调用实例方法
- 实例方法中也可以调用类方法(通过类名)
category 和 extension 的区别
- 分类有名字,类扩展没有分类名字,是一种特殊的分类
- 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成员变量和方法
Foundation对象与CoreFoundation对象有什么区别
-
Foundation
对象是OC的,在MRC下需要手动管理内存,ARC下不需要手动管理 -
Core Foundation
对象是C对象,MRC和ARC都需要手动管理内存 -
Foundation对象 和 Core Foundation对象间的转换:俗称桥接
ARC环境桥接关键字:
// 可用于Foundation对象 和 Core Foundation对象间的转换 __bridge // 用于Foundation对象 转成 Core Foundation对象 __bridge_retained // Core Foundation对象 转成 Foundation对象 __bridge_transfer
-
Foundation对象 转成 Core Foundation对象
-
使用
__bridge
桥接- 如果使用
__bridge
桥接,它仅仅
是将strOC的地址
给了strC, 并没有
转移对象的所有权,也就是说, 如果使用__bridge桥接, 那么如果strOC释放了,strC也不能用了 - 注意:在ARC条件下,如果是使用__bridge桥接,那么strC
可以不用主动释放
, 因为ARC会自动管理strOC和strC
NSString *strOC1 = [NSString stringWithFormat:@"abcdefg"]; CFStringRef strC1 = (__bridge CFStringRef)strOC1; NSLog(@"%@ %@", strOC1, strC1);
- 如果使用
-
使用
__bridge_retained
桥接- 如果使用__bridge_retained桥接,它会将对象的
所有权转移
给strC, 也就是说,即便strOC被释放了, strC也可以使用
- 注意:在ARC条件下,如果是使用__bridge_retained桥接,那么strC
必须自己手动释放
,因为桥接的时候已经将对象的所有权转移给了strC,而C语言的东西不是不归ARC管理的
NSString *strOC2 = [NSString stringWithFormat:@"abcdefg"]; // CFStringRef strC2 = (__bridge_retained CFStringRef)strOC2; CFStringRef strC2 = CFBridgingRetain(strOC2);// 这一句, 就等同于上一句 CFRelease(strC2);
- 如果使用__bridge_retained桥接,它会将对象的
-
-
Core Foundation对象 转成 Foundation对象
-
使用__bridge桥接
- 如果使用__bridge桥接,它
仅仅
是将strC的地址
给了strOC, 并没有
转移对象的所有权 - 也就是说如果使用__bridge桥接,那么如果strC释放了,strOC也不能用了
CFStringRef strC3 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII); NSString *strOC3 = (__bridge NSString *)strC3; CFRelease(strC3);
- 如果使用__bridge桥接,它
-
使用__bridge_transfer桥接
- 如果使用__bridge_transfer桥接,它会将对象的
所有权转移
给strOC, 也就是说,即便strC被释放了, strOC也可以使用
- 如果使用__bridge_transfer桥接, 他会自动释放strC, 也就是以后我们
不用手动释放strC
CFStringRef strC4 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII); // NSString *strOC = (__bridge_transfer NSString *)strC; NSString *strOC4 = CFBridgingRelease(strC4); // 这一句, 就等同于上一句
- 如果使用__bridge_transfer桥接,它会将对象的
-
-
NSNotification、Block、Delegate、KVO的区别
- Block和Delegate属于代理模式,是一对一的关系,Notification和KVO是观察者模式,是一对多的关系
- 代理模式相对观察者模式效率比较高
- notification通过维护一个array,实现一对多消息的转发
- Delegate需要定义协议方法,代理对象实现协议方法,需要两者之间必须建立联系,不然没法调用代理的方法;
- notification不需要两者之间有联系
- Block更加简洁,不需要定义繁琐的协议方法,但通信事件比较多的话,建议使用Delegate
__block
和__weak
修饰符的区别?
__block
不管是ARC还是MRC模式下都
可以使用,可以
修饰对象,也可以
修饰基本数据类型__weak
只能在ARC模式下使用,只能
修饰对象(NSString),不能
修饰基本数据类型__block
修饰的对象可以在block中被重新赋值,__weak
修饰的对象不可以
postNotification是同步调用还是异步调用?
- 同步调用。
- 当调用addObserver方法监听通知,然后调用postNotification抛通知,postNotification会在当前线程遍历所有的观察者,然后依次调用观察者的监听方法
- 调用完成后才会去执行postNotification后面的代码。
如何实现异步监听通知?
通过addObserverForName:object:queue:usingBlock来实现异步通知。
是否可以把比较耗时的操作放在 NSNotification中
- 首先必须
明确通知在哪个线程中发出
,那么处理接受到通知的方法也在这个线程中调用 - 如果在
异步
线程发的通知,那么可以
执行比较耗时的操作; - 如果在
主线程
发的通知,那么就不可以
执行比较耗时的操作
类别的作用?
category 可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改,并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。类别不能添加实例变量
-
将类的实现分散到多个不同文件或多个不同框架中
-
创建对私有方法的前向引用
-
向对象添加非正式协议
C和 OC 如何混编
- .m文件 可以编写 OC语言 和 C 语言代码
- .cpp文件 只能识别C++ 或者C语言(C++兼容C)
- .mm文件 主要用于混编 C++和OC代码,可以同时识别OC,C,C++代码
Swift 和OC 如何调用
- Swift 调用 OC代码
需要创建一个
<Module>-Bridging-Header.h
的桥接文件,在文件内引入需要调用的OC代码头文件 - OC 调用 Swift代码
直接引入
<Module>-Swift.h
文件。Swift如果需要被OC调用,使用@objc 对方法或者属性进行修饰
OC中定义一个枚举的几种方法
- 因为 OC 是兼容 C 的,所以可以使用 C 语言风格的 enum 进行定义
- 使用
NS_ENUM
宏进行定义 - 使用
NS_OPTIONS
宏进行定义
NS_ENUM
为定义通用性枚举,只能单选。NS_OPTIONS
为定义位移枚举,可多选
怎么理解协议以及协议的默认修饰符
- 协议规定了一系列的行为(方法)和数据(属性),主要用在代理模式中
- OC中协议默认是
@required
必须实现的,使用@optional
修饰后变为可选择实现
OC中集合的注意事项和相互之间的区别
NSArray/NSMuatbleArray/NSPointerArray
-
NSArray 初始化后,元素就不可再增删
-
NSMuatbleArray 初始化后,可以随时添加或删除元素对象
-
NSPointerArray 与
NSMuatbleArray
相似,区别是可以指定对元素的强/弱引用
indexOfObject
与 indexOfObjectIdenticalTo
的区别
- 都是来判断某一对象是否是
Array
集合内的元素,如果是则返回该对象在集合内的索引 - 区别就在于两个 API 判定的依据
indexOfObject
会使用isEqualTo
方法将Object
与集合元素进行比较indexOfObjectIdenticalTo
则会比较Object
与集合元素的指针是否相同
NSDictionary/NSMutableDictionary/NSMapTable
- NSDictionary 初始化后就不可再增删键值对
- NSMutableDictionary 初始化后可以随时添加或删除键值对
- NSMapTable 类似
NSMutableDictionary
可以指定对value
的强/弱引用 - 当某个键值对添加到
Dictionary
时,Dictionary
会对key
进行深拷贝,而对value
进行浅拷贝,已经添加到集合内部的键值对,key 值将不可更改 - 作为
key
的对象都需要满足哪些条件呢key
必须遵循NSCopying
协议,因为当元素渐入字典后,会对key
进行深拷贝key
必须实现hash
与isEqual
方法
NSSet/NSMuatbleSet/NSCountedSet/NSHashTable
- NSSet 初始化后就不可再增删元素
- NSMutableSet 初始化后可随时增删元素
- NSCountedSet 每个元素都带有一个计数,添加元素,计数为一。重复添加某个元素则计数加一;移除元素计数减一,计数为零则移除
- NSHashTable 和 NSMutableSet 类似,区别是可指定对元素的强/弱引用
NSMutableDictionary 中使用setValueForKey 和 setObjectForKey有什么区别
- 一般情况下,给NSMutableDictionary 发送
setValue
仍然是调用了setObject
方法,如果参数 value 为 nil,则会调用removeObject
移除这个键值对 setObjectForKey
是 NSMutableDictionary特有的,value 不能为nil,否则会崩溃setValueForKey
是KVC的方法,key 必须是字符串类型,setObject 的 key 可以是任意类型
NSCache 和NSDictionary 区别
- NSDictionary的key必须符合nscopying协议,在设置值时会copy key
- NSCache可以提供自动删减缓存功能,而且保证线程安全,与字典不同,不会拷贝键
- NSPurgeableData搭配NSCache使用,可以自动清除数据
NSArray 和 NSSet区别
- NSSet和NSArray功能性质一样,用于存储对象,属于集合。
- NSSet,NSArray都是类,只能添加对象,如果需要加入基本数据类型(int,float,BOOL,double等),需要将数据封装成NSNumber类型
- NSSet属于 “无序集合”,采用hash算法计算存储位置,查询速度快,保证了元素的唯一性,在内存中存储方式是不连续的
- NSArray是 “有序集合” 它内存中存储位置是连续的
OC类可以多继承么?可以实现多个接口么?
-
OC的类不可以有多继承,OC里面都是单继承
-
多继承可以用protocol委托代理来模拟实现,可以通过实现多个接口完成OC的多重继承
为什么不要在Category中重写一个类原有的方法
-
Category没有办法去代替子类,它不能像子类一样通过super去调用父类的方法实现。
如果Category中重写覆盖了当前类中的某个方法,那么这个当前类中的原始方法实现,将永远不会被执行,这在某些方法里是致命的
-
一个Category也不能可靠的覆盖另一个Category中相同的类的相同的方法。
例如UIViewController+A与UIViewController+B,都重写了viewDidLoad,我们就无法控制谁覆盖了谁
-
要重写方法,我们首推通过子类重写父类的方法,在一些不方便重写的情况下,我们也可以在category中用runtime进行method swizzling(方法的偷梁换柱)来实现。
OC成员变量的修饰符及作用范围
@private
,本类可以访问,子类和其他类不可以访问@protected(默认修饰符)
,本类和子类可以访问,其他类不可以访问@package
,对于framework内部相当于@protected
,对于framework外部相当于@private
@public
,本类、子类、其他类都可以访问
@proprety的本质和作用
@property = ivar + getter + setter
- 在.h文件自动生成
getter/setter
方法声明 - 在.m文件生成成员变量和
getter/setter
方法的实现
- 在.h文件自动生成
- 可以手动实现
getter/setter
方法。如果同时实现了getter和setter
,那么还需要指定成员变量 - 也可以使用
@synthesize myName = myString
自己指定成员变量
@proprety修饰符说明
- 原子性:
atomic/nonatomic
-
默认为
atomic
,系统会自动加上同步锁(自旋锁),影响性能 -
建议使用
nonatomic
,可以提高访问的性能
-
- 读写权限
readonly
,readwrite
- 指定读写方法
getter
,setter
- 内存管理语义
strong
,assign
,weak
,copy
,unsafe_unretained
- 是否可以为空
nullable
,nonnull
,null_resettable
,null_unspecified
- 类属性
class
ARC下,不显式指定属性关键字时,默认的关键字都有哪些
- 基本数据类型:
atomic, readwrite, assign
- 普通的OC 对象:
atomic, readwrite, strong
atomic、nonatomic区别以及作用
-
主要区别就是系统自动生成的
getter/setter
方法不一样atomic
系统自动生成的getter/setter
方法会进行加锁操作nonatomic
系统自动生成的getter/setter
方法不会进行加锁操作
-
atomic
不是线程安全的- 系统生成的
getter/setter
方法会进行加锁操作,**注意:**这个锁仅仅保证了getter/setter
存取方法的线程安全 atomic
可以保证多线程访问时,对象是未被其他线程销毁的(比如:如果当一个线程正在get或set时,又有另一个线程同时在进行release操作,可能会直接crash)
- 系统生成的
讲一下atomic的实现机制
- atomic 表示是原子性的,用来声明属性时,编译器自动生成getter/setter方法,最终会调用
objc_getProperty
和objc_setProperty
方法来进行存取属性 - 这两个方法内部使用了
os_unfair_lock
进行加锁,来保证读写的原子性。 - 锁都在
PropertyLocks
中保存着(在iOS平台会初始化8个,mac平台64个)。在用之前,会把锁都初始化好 - 在需要用到时,用对象的地址加上成员变量的偏移量为key,去
PropertyLocks
中去取。因此存取时用的是同一个锁,所以atomic能保证属性的存取时是线程安全的。 - 注:由于锁是有限的,不同对象,不同属性的读取用的也可能是同一个锁
atomic为什么不能保证绝对的线程安全?
- atomic在getter/setter方法中加锁,仅保证了存取时的线程安全
- 假设我们的属性是
@property(atomic)NSMutableArray *array;
可变的容器时,无法保证对容器的修改是线程安全的
什么情况使用 weak 关键字,相比 assign 有什么不同
什么情况使用 weak 关键字
- 在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如:delegate代理属性,代理属性也可使用assign
- 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性一般也使用weak;当然,也可以使用strong,但是建议使用weak
weak 和 assign 的不同点
weak
:在属性所指的对象遭到销毁时,系统会将weak
修饰的属性指向nil
,在OC
给nil
发消息是不会有什么问题的assign
:在属性所指的对象遭到销毁时,assign
属性还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候再给此对象发送消息,很容造成程序崩溃assign
可以用于修饰非OC
对象,而weak
必须用于OC
对象
代理使用 weak 还是 assign
- 建议使用
weak
,它指明该对象并不负责保持delegate这个对象,delegate这个对象的销毁由外部控制 - 可以使用
assign
,也有weak的效果,对于使用 assign修饰的delegate,在对象释放前需要将 delegate 指针设置为 nil,不然会产生野指针
用@property声明的NSString经常使用copy关键字,为什么
NSString、NSArray、NSDictionary 等经常使用 copy
关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary
- 因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本
- 他们之间可能进行赋值操作(就是把可变的赋值给不可变的),为确保对象中的属性值不会无意间变动,应该在设置新属性值时拷贝一份,保护其封装性
这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray *arr
;
- 问题:添加、删除、修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃
- 原因:是因为 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加、修改
如何让自定义类可以用 copy 修饰符
- 若想令自己所写的对象具有拷贝功能,需实现
NSCopying
协议 - 如果自定义的对象分为可变版本与不可变版本,那么就要同时实现
NSCopyiog 与 NSMutableCopying 协议
// 实现不可变版本拷贝
- (id)copyWithZone:(NSZone *)zone;
// 实现可变版本拷贝
- (id)mutableCopyWithZone:(NSZone *)zone;
// 重写带 copy 关键字的 setter
- (void)setName:(NSString *)name {
_name = [name copy];
}
在某个方法中 self.name = _name,name = _name 它 们有区别吗,为什么?
- 前者是存在内存管理的setter方法赋值,它会对_name对象进行保留或者拷贝操作
- 后者是普通的变量赋值
@synthesize 和 @dynamic 分别有什么作用
- @property 有两个对应的词,一个是
@synthesize
,一个是@dynamic
- 如果
@synthesize 和@dynamic
都没写,那么默认的就是@syntheszie var = _var
@synthesize
如果你没有手动实现getter/setter
方法,那么编译器会自动为你加上这两个方法@dynamic
告诉编译器属性的getter/setter
方法由用户自己实现,不自动生成(当然对于 readonly 的属性只需提供 getter 即可)
@synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么
- 如果指定了成员变量的名称,会生成一个指定的名称的成员变量
- 如果这个成员已经存在了就不再生成了
- 如果是
@synthesize foo;
还会生成一个名称为foo的成员变量,也就是说:如果没有指定成员变量的名称会自动生成一个属性同名的成员变量
在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景
- 同时重写了 setter 和 getter 时
- 重写了只读属性的 getter 时
- 使用了 @dynamic 时
- 在 @protocol 中定义的所有属性
- 在 category 中定义的所有属性
- 重载的属性,当你在子类中重载了父类中的属性,
必须
使用@synthesize来手动合成ivar
什么是沙盒机制
每个iOS程序都有一个独立的文件系统(存储空间),而且只能在对应的文件系统中进行操作,此区域被称为沙盒。应用必须待在自己的沙盒里,其他应用不能访问该沙盒
沙盒目录结构是怎样的?
- Application:存放程序源文件,上架前经过数字签名,上架后不可修改
- Documents:常用目录,iCloud
备份
目录,存放数据 - Library
- Caches:存放体积大又不需要备份的数据
- Preference:设置目录,iCloud会
备份
设置信息
- tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能
随时
被清除的可能
说一下OC的反射机制
在OC中,反射是指程序在运行时,获取和修改类的信息,基于OC的Runtime机制,主要从三个层面使用反射
- NSObject类集成的一些反射API
- 系统Foundation框架提供了一些方法反射的API
- 使用objc/runtime提供的一些API
反射常用的方法
- isMemberOfClass // 对象是否是某个类型的对象
- isKindOfClass // 对象是否是某个类型或某个类型子类的对象
- respondsToSelector // 是否能响应某个方法
- conformsToProtocol // 是否遵循某个协议
class方法和object_getClass方法有什么区别?
- 实例class方法就直接返回object_getClass(self),类class方法直接返回self
- object_getClass(类对象),则返回的isa指向的元类
反射的应用场景
- JSON与模型之间的相互转换
- Method Swizzling
- KVO的实现原理
- 实现NSCoding的自动归档和自动解档
实现description方法能取到什么效果
当使用log打印该对象或断点po对象时,可以详细的知道该对象的信息,方便代码调试
静态库和动态库的区别
静态库
- 以.a 和 .framework为文件后缀名
- .a 主要是二进制文件,不包含资源。需要自己添加头文件
- .framework 可以包含头文件 + 资源信息
好处
- 可以模块化,分工合作
- 能避免少量改动经常导致大量的重复编译连接
- 也可以重用,注意不是共享使用
动态库
- 以.tbd(之前叫.dylib) 和 .framework 为文件后缀名
好处
- 可以将最终可执行文件体积缩小
- 多个应用程序共享内存中的同一份库文件,节省资源
- 可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的
区别
静态库
- 链接时会被完整的复制到可执行文件中,被多次使用就有多份拷贝
动态库
- 链接时不复制,程序运行时由系统动态加载到内存,系统只加载一次,多个程序共用,节省内存
代理的作用
代理又叫委托,是一种设计模式,是对象与对象之间的通信交互,解除了对象之间的耦合性
- 改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度
为什么代理要用weak?
- 避免循环引用。
- 如果使用
strong
修饰且代理是self的话,会出现循环引用,从而造成内存泄漏
代理的delegate和dataSource有什么区别?
- 代理的
delegate
和dataSource
是为了职责分离delegate
负责事件处理datasource
负责数据源处理
block和代理的区别
delegate
和block
都可以实现回调传值block
写法简练,可以直接访问上下文,代码阅读性好,适合与状态无关的操作,更加面向结果,使用过程中需要注意避免造成循环引用。delegate
更像一个生产流水线,每个回调方法是生产线上的一个处理步骤,一个回调的变动可能会引起另一个回调的变动,其更加面向过程
- 一般情况下,简单功能的回调用block,3个以上系列函数的回调选择delegate
- delegate运行成本低,block的运行成本高
- block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除。
- delegate只是保存了一个对象指针,直接回调,没有额外消耗。就像C的函数指针,只多做了一个查表动作
对self = [super init]的理解
[super init] 到底做了什么?
[super init] 是面向对象的体现,调用父类的init方法来为子类从父类继承来的属性进行初始化
当init方法需要重新alloc一块空间时,正确的写法如下
- (id) init {
id tmp = self;
// [self class]将获得self指向的实例对应的类实例
self = [[self class] alloc];
[tmp release];
//other staffs
return self;
}
为什么把**[super init]**的值赋值给self?
为了防止父类的初始化方法释放了self指向的空间后,又重新alloc了一块空间。这样的话,[super init]
可能alloc失败,这时就不再执行if中的语句。
super作为消息接受者的实质是什么
super并不是真正的指针,[super message]的实质是由self来接受父类的message。
**switch **语句 **if **语句区别与联系
- 均表示条件的判断
- switch语句表达式只能处理整型、字符型和枚举类型,而选择流程语句则没有这样的限制。
- switch语句比选择流程控制语句效率更高
BAD_ACCESS在什么情况下出现
这种问题在开发时经常遇到。原因是访问了野指针,比如访问已经释放对象的成员变量或者发消息、死循环等
结构体与数组有什么区别?
- 结构体可以存不同类型的元素,而数组只能存同一类型
- 结构体类型需要我们自已定义,数组是用别的类型加 [元素个数]
- 结构体内存分配方式很特别,使用对齐原则,不一定是所有元素的字节数和,而数组一定是所有元素的字节数和。
- 结构体指针可以指针名->结构体元素名(取元素);数组不行
浅复制和深复制的区别
- 浅复制:只复制指向对象的指针,而不复制引用对象本身
- 深复制:复制引用对象本身,会在堆区重新开辟新的内存块
- 通俗的讲:浅复制好比你和你的影子,你完蛋,你的影子也完蛋;深复制好比你和你的克隆人,你完蛋,你的克隆人还活着
集合类型的深浅拷贝
数据类型 | copy | multableCopy |
---|---|---|
不可变类型 | 浅拷贝 | 单层深拷贝 |
可变类型 | 单层深拷贝 | 单层深拷贝 |
iOS 方法内局部变量 (对象) 的内存释放过程
- 在
ARC
环境下,方法在将要执行结束的时候,局部变量的指针都会被置为nil
- 此时在
objc_storeStrong
函数内部,被局部变量强引用的对象会被执行release
操作。 - 最终对象是否会被释放还是要取决于是否依旧有其他指针强引用 (比如: 全局变量/属性 等)
KVC中的集合运算符
简单集合操作符
作用于Array或者Set中相对于集合操作符右侧的属性(只能用在集合对象中,对象属性必须为数字类型)
- @avg:将集合中属性键路径所指对象转换为 double,计算其平均值,返回该平均值的 NSNumber 对象。当均值为 nil 的时候,返回 0
- @count :返回集合中对象总数的 NSNumber 对象。操作符右边没有键路径
- @max :比较由操作符右边的键路径指定的属性值,并返回比较结果的最大值。
- 最大值由指定的键路径所指对象的 compare: 方法决定,因此参加比较的对象必须支持和另一个对象的比较。
- 如果右侧键路径所指对象值为 nil, 则忽略,不影响比较结果。
- @min 和 @max 一样,但是返回的是集合中的最小值。
- @sum 返回右侧键路径指定的属性值的总和。
- 每一个比较值都转换为 double,然后计算值的总和,最后返回总和值的 NSNumber 对象。
- 如果右侧键路径所指对象值为 nil,则忽略。
对象操作符
- @distinctUnionOfObjects 和 @unionOfObjects, 返回一个由操作符右边的 key path 所指定的对象属性组成的数组。其中 @distinctUnionOfObjects 会对数组去重,而 @unionOfObjects 不会。
数组和集合操作符
- 作用对象是嵌套的集合,也就是说,是一个集合且其内部每个元素是一个集合
@distinctUnionOfArrays / @unionOfArrays
返回一个数组,其中包含这个集合中每个数组对于这个操作符右面指定的 key path 进行操作之后的值。 distinct 版本会移除重复的值。@distinctUnionOfSets
和 @distinctUnionOfArrays 差不多, 但是它期望的是一个包含着 NSSet 对象的 NSSet ,并且会返回一个 NSSet 对象。因为集合不能包含重复的值,所以它只有 distinct 操作。
哪些情况会导致App崩溃,可以用什么方法拦截并化解?
- unrecognized selector sent to instance 方法找不到
- 数组越界,插入空值
[NSDictionary initWithObjects:forKeys:]
使用此方法初始化字典时,objects和keys的数量不一致时- NSMutableDictionary,
setObject:forKey:
或者removeObjectForKey:
时,key为nil setValue:forUndefinedKey:
,使用KVC对对象进行存取值时传入错误的key或者对不可变字典进行赋值- NSUserDefaults 存储时key为nil
- 对字符串操作时,传递的下标超出范围,判断是否存在前缀,后缀子串时,子串为空
- 使用C字符串初始化字符串时,传入null
- 对可变集合或字符串使用copy修饰并进行修改操作
- 在空间未添加到父元素上之前,就使用autoLayout进行布局
- KVO在对象销毁时,没有移除KVO或者多次移除KVO
- 野指针访问
- 死锁
- 除0
XML数据解析方式各有什么不同?
DOM解析
- 必须完成DOM树的构造,在处理规模较大的XML文档时就很耗内存,占用资源较多
- 读入整个XML文档并构建一个驻留内存的树结构(节点树)
- 通过遍历树结构可以检索任意XML节点,读取它的属性和值
- 通常情况下,可以借助XPath查询XML节点
SAX解析
-
它是事件驱动模型
-
解析XML文档时每遇到一个开始或者结束标签、属性或者一条指令时,程序就产生一个事件进行相应的处理
-
一边读取XML文档一边处理,不必等整个文档加载完才采取措施
-
当在读取解析过程中遇到需要处理的对象,会发出通知进行处理。
SAX相对于DOM来说更适合操作大的XML文档。
OC与 JS交互方式有哪些?
-
通过拦截URL, 在onStart等方法里编写逻辑
-
使用MessageHandler(WKWebView)
- 当JS端想传一些数据给iOS,
window.webkit.messageHandlers.<方法名>.postMessage(<数据>)
- 在OC中处理WKScriptMessageHandler的代理方法。name和上方JS中的方法名相对应
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
- 当JS端想传一些数据给iOS,
-
JavaScriptCore (UIWebView)
-
使用三方库WebViewJavascriptBridge,可提供 js 调OC以及O调JS
1. 设置 webViewBridge _bridge = [WKWebViewJavascriptBridge bridgeForWebView:self.webView]; [_bridge setWebViewDelegate:self]; 2. 注册handler方法,需要和 前段协商好 方法名字,是供 JS调用Native 使用的。 [_bridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) { // OC调用 NSString *scanResult = @"http://www.baidu.com"; // js 回调传参 responseCallback(scanResult); }]; 3. OC掉用JS [_bridge callHandler:@"testJSFunction" data:@"一个字符串" responseCallback:^(id responseData) { NSLog(@"调用完JS后的回调:%@",responseData); }];
说一下iOS 中的APNS远程推送原理
- iOS 系统向苹果的APNS服务器请求手机端的
deviceToken
- App接收到手机端的deviceToken,然后通过接口传递给服务器
- App服务器需要发送推送消息时,构建消息体完成后,将消息体、需要接收推送的用户的devicetokens传递给APNS 服务器
- APNS 服务器根据对应的 deviceToken 发送到用户的手机上
以下代码打印结果是什么
NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];
BOOL flag = NO;
[userdefault setObject:@(flag) forKey:@"flag"];
if ([userdefault objectForKey:@"flag"]) {
BOOL eq = [userdefault objectForKey:@"flag"];
if (eq) {
NSLog(@"a");
}
else {
NSLog(@"b");
}
}
else {
BOOL eq = [userdefault objectForKey:@"flag"];
if (eq) {
NSLog(@"c");
}
else {
NSLog(@"d");
}
}
打印结果 a
- flag被包装成 oc 对象(NSNumber),OC对象有值,转 BOOL 都是 YES
- 这里应该使用boolForKey