关键词defer的用法?
答案:defer的字面意思是延时执行,在swift中修饰一个block,该block内的代码会在当前函数的所有代码执行完毕之后才会被执行。
Swift中的访问权限?
答案:open public internal fileprivate private
open: open修饰的类可以在它们被定义的模块中被继承,也能在引入此定义模块中的其他模块中被继承;open修饰的类成员或者方法可以在它们被定义的模块中被重写,也能在引入此定义模块中的其他模块中被重写;
public: public修饰的类可以在它们被定义的模块中被继承,不能在引入此定义模块中的其他模块中被继承;public修饰的类成员或者方法可以在它们被定义的模块中被重写,不能在引入此定义模块中的其他模块中被重写
interanal:默认的访问级别,可以在模块内的任何地方被访问
filePrivate:在不同的.swift中不能被访问,在同一个.swift中可以访问
private: 只允许当前对象的当前.swift 文件访问
Class 和 Struct 的区别?
类和结构体的不同点:
- 类可以继承而结构体不行。
- 结构体(和枚举)是值类型, 而类是引用类型,引用计数允许对实例进行多个引用,结构体的持有者只能有一个。
类和结构体的共同点:
- 定义属性
- 定义提供功能的方法
- 可以用下标点来访问值
- 可以进行扩展
- 可以遵循协议
@proprety的本质是什么?
答案:@property = ivar(实例变量)+getter + setter(存取方法)
“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)
如何给 Category 添加属性?
Objective-C 中的 Category它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。在 Category 中增加属性的目的主要为了解耦,简化框架调用,在很多第三方框架中会使用。
分类特点:
- 分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表。原则上讲它只能添加方法, 不能添加属性(成员变量),实际上可以通过其它方式添加属性 ;
- 分类中的可以写@property, 但不会生成setter/getter方法, 也不会生成实现以及私有的成员变量,会编译通过,但是引用变量会报错;
- 如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法,同名方法调用的优先级为 分类 > 本类 > 父类;
OC runtime动态绑定变量的方法:
objc_getAssociatedObject(<#T##object: Any##Any#>, <#T##key: UnsafeRawPointer##UnsafeRawPointer#>)
objc_setAssociatedObject(<#T##object: Any##Any#>, <#T##key: UnsafeRawPointer##UnsafeRawPointer#>, <#T##value: Any?##Any?#>, <#T##policy: objc_AssociationPolicy##objc_AssociationPolicy#>)
Explain [weak self] and [unowned self]?
- weak 和unowned都是用来解决引用循环的,都不会对对象进行强引用
- weak用的是weak 引用,unowned用的是unowend的引用
- weak引用会在对象没有强引用的时候,把weak指针全部销毁,而unowned 引用在对象没有强引用的时候不会销毁,这时如果通过unowned引用去访问这个对象会造成运行时的错误。
- weak修饰的对象是可选类型的,而owned修饰的对象期望是一直有值的是非可选性的。
- 在其他的实例对象比当前实例对象生命周期短,其他对象先被释放的时候用weak,相反在当其他实例对象和当前实例对象用相同的生命周期或者更长的生命周期的时候用unowned。
Extension in swift?
答案:类,结构体,枚举,协议都可以进行扩展
- 扩展实例方法和类方法
- 添加计算型属性
- 添加初始化的方法
- 在扩展中遵循协议
Non-Escaping and Escaping Closures?
例如一个闭包作为一个方法的参数,在这个函数在这个方法执行结束的时候这个闭包没有被调用或者是延时调用,这时候系统会提示加上@escaping关键字标记为逃逸闭包
swift中可选类型的本质是什么?
一个为Optional的枚举 一个case是 none ,另一个case 是 some(wrapped)枚举关联的值
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
/// The absence of a value.
///
/// In code, the absence of a value is typically written using the `nil`
/// literal rather than the explicit `.none` enumeration case.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
}
map vs flatmap
都是接受一个闭包对数组每个元素进行遍历,进行相同的操作返回一个新的数组。Flatmap中没有为nil的元素,flatmap可以对数组降维。
filter VS reduce
filter接受一个过滤条件的闭包,返回一个符合过滤条件的数组
reduce给定义一个初始值,根据闭包处理这些元素。合并处理的结果
Define typealais ?
用 typealais name = existing type
一个type的alais声明之后,可以用声明的名字来替代现有的类型。可以是已经存在的类类型或者是复合类型,类型别名不创建新的类型,他只是一个涉及现有类型的名字。
Type safety & Type Inference? //类型安全和类型推断
类型安全: swift是个类型安全的语言,类型安全的语言鼓励我们明确值的类型,不能像一个字符串传递一个整数。
类型推断: 当你没有指定值的类型的时候,swift会根据你赋的值推断出合适的类型。
Explain Forced Unwrapping ?
当我将一个变量定义为可选类型的时候,我们要从中获取到值,我们需要在一个变量后面加上!
Optional binding ? //可选绑定
答案:当使用一个可选类型的变量,我们想知道改变量是否包含值,如果包含用该值作为临时的常量或者变量 if let realValue = optionalValue { }
Optional Chaining ?
答案:是一个查询调用属性,方法,下标的的过程,当一个可选项包含值,则属性,方法,下标调用成功,如果为nil就返回nil。多个查询链拼在一起的时候,任何一个链为nil ,则返回nil
closures VS blocks?
闭包:当一个函数函数定义在外层作用域中,比如一个函数作为另一个函数的返回值,它能够捕获局部变量,这些局部变量会存在函数之中不会随着局部作用域的结束而销毁,这样的函数叫做闭包。
Swift闭包表达式和函数是相同的类型,你可以函数传递给闭包;
Swift闭包和block都具有捕获语义,变量是可变的而不是复制的,OC中的__block 在swift中是默认行为。 __block 将对象从堆上copy到栈上

Explain Identical operator ?//相等的判断
答案:!==和 ===
判断是否引用相同的内存地址
是判断引用相等在OC中相当于==
==和!=
判断值相等用于值类型
OC中是 isEqual:
What is Downcasting ?//类型向下转化
答案: as 用类型向上转化,或者是桥接类型
as? 安全转化,如果失败返回nil
as! 强制转化,如果失败就会崩溃
What are benefits of Guard ?
答案:避免陷入一层层的if判断中
Explain generics in Swift ?
答案:用泛型所写的代码不必指明其准确的数据类型
What is the Swift main advantage ?
答案:1.可选类型可以避免一些闪退
2.类型安全的语言
3.统一的内存管理,全部用的是ARC
How is an “inout” parameter different from a regular parameter?
答案: inout通过引用传递,常规参数通过值传递
Define Properties in swift ?
答案:储存属性将常量或者变量作为实例的一部分储存,计算性属性计算一个值,不作为实例的一部分存储,计算性属性可以用在结构体,类,枚举,而储存性属性只能用在结构体和类
Explain Stored Property?
答案:存储属性var let ; 注意的是,一个结构体被声明为常量,即使里面的储存属性为变量也不能修改,因为结构体是值类型的。类是引用类型就是可以修改。
Explain Computed Property ?
答案:类,结构体,枚举都可以定义计算性属性,这些属性都不储存值,
他提供一个get 和一个可选的set 方法;
当一个计算型属性只有get方法,没有set,一个只读的计算性属性,总是返回值。
只能用var 每次计算都可能得到不同的值
Explain Lazy stored Property ?
答案:延时储存性属性仅在第一次调用改属性时计算。
Lazy变量初始化不是自动的,所以不是线程安全的
Explain Property observers ?
答案:willset { 用将要设置的 newValue } didset { 之前的oldValue }
Explain Default Property Values ?
答案: 如果一个属性有默认值,就不用在初始化的时候从新设置它的值
Expain Default Initializer ?
答案:swift为类和结构体提供默认的初始化方法,为属性提供默认的值,不止提供一个初始化的方法。默认初始化方法创建一个实例为它的所有属性设置默认值。
Explain Initializer Delegation for Value Types ?
答案:初始化委托,一个初始化的方法里面可以调用其它的初始化方法,来初始化一个实例。这个过程我们叫做初始化委托,可以可以避免重复的初始化代码。指定初始化方法,可以避免使用默认初始化的时候的绕过一些属性的设置。
Explain Initialiser designation for class types ? //初始化委托
答案:Rule1: 一个指定初始化必须要调用父类的指定初始化方法
Rule2: 一个类的便利初始化方法,必须要调用该类的其他初始化方法
Rule3:便利初始化方法最终必须调用该类的指定初始化方法
Explain Two Phase Initialization ?//解释两部分初始化
答案:第一个部分是现在自己的储存性属性赋值,
第二部分 每个类有修改储存属性值的机会
安全检查1:一个指定初始化方法必须确保自己的属性有值,才能去调用父类的初始化
安全检查2:在修改继承父类的属性之前一定要调用父类的初始化方法,如果不这样子类修改的值,可能被父类的初始化再次修改
安全检查3:一个便利初始化方法在给属性赋值的时候必须先调用委托本类的初始化方法,如果不这样便利初始化的属性可能被指定初始化所修改
安全检查4:在初始化方法的第一步未完成之前不能调用任何势力方法,或者self先关的的值
Explain Automatic Initializer Inheritance?
答案:swift默认情况下是不继承父类的初始化方法的,如果遇到特殊条件可以继承
条件1:如果你的子类没有定义指初始化方法,子类继承父类的所有指定初始化方法
条件2 如果子类实现了父类的所有指定初始化方法,子类就继承了父类的所有便利初始化方法
image.png
Explain Failable initialisers ?
答案:可能会初始化失败的初始化方法,用init?来表示:
例如,一个用枚举的rawValue初始化来初始化一个枚举,当这个rawValue不在枚举的范围内就会返回一个nil
Explain Initializing with closures ?
答案:相当于调用一个有返回值的func
Explain Enum and associated value ?
答案:你可以用swift的枚举来关联任何给定类型的关联值,关联值的类型可以不同类型的。
Explain Implicitly Assigned Raw Values ?
答案:当初处理整数或者字符串的枚举时,不必为每种情况分配原始值,swift会自动为你分配值。
Explain Difference between UIWindow and UIView ?
答案:UIwindow没有可视的内容,只是给视图提供一个可视的容器
What is UIButton hierarchy ?
答案: UIButton -> UIControl-> UIView -> UIResponder -> NSObject

内存中的5大区分别是什么?
• 栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 操作方式类似于数据结构中的栈。
• 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
• 全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。
• 文字常量区:常量字符串就是放在这里的。 程序结束后由系统释放。
• 程序代码区:存放函数体的二进制代码。
指针和引用的区别?
如果电脑的存储器相当于一本书,那么指针就像粘在每一页的页码,想要哪页的内容直接找页码直接就会找到。
*事件传递以及响应机制?
事件响应:
- 如果当前view是控制器的view,那么控制器就是上一个响应者,事件就传递给控制器;如果当前view不是控制器的view,那么父视图就是当前view的上一个响应者,事件就传递给它的父视图
- 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
- 如果window对象也不处理,则其将事件或消息传递给UIApplication对象
-
如果UIApplication也不能处理该事件或消息,则将其丢弃
image.png
事件传递:
- 比如点击一个view,当发生触摸事件之后,系统会利用runloop将事件添加到UIApplication管理的队列中(即首先受到事件的是UIApplication)
- UIApplication会从事件队列中取出最前面的触摸事件分发给应用程序的主窗口keyWindow
- keyWinow会在视图层次结构中找到一个最合适的视图来处理触摸事件
如何找到最合适的视图来出时间:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
super.hitTest(point, with: event)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
super.point(inside: point, with: event)
}
UIApplication事件队列->[UIWindow hitTest:withEvent:]->返回更合适的view->[子控件 hitTest:withEvent:]->返回最合适的view
pointInside:withEvent:方法判断点在不在当前view上(方法调用者的坐标系上)如果返回YES,代表点在方法调用者的坐标系上;返回NO代表点不在方法调用者的坐标系上,那么方法调用者也就不能处理事件。
事件的传递和响应的区别:
事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。
参考链接
autoreleasepool 什么时候释放?
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是 -2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 autoreleasepool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 pool 了。
iOS编译都做了什么?
两者都需要通过编译器(Clang + LLVM)把代码编译器生成机器码。
推送的原理?

- app向iOS设备发送注册的通知,用户需要同意发送推送
- iOS向apns远程推送服务器发送bundleid和设备的udid
- apns根据设备的udid和bundleid生成devicetoken返回给app
- app再将自己的deviceToken发送自己的服务器,由服务器保存在数据库中
- 当需要推送的时候,自己的服务器再根据devicetoken发送给apns
- apns在根据devicetoken发送给对应的app
0C的id对应swift any 还是anyobject?
答案:是anyobject
any可以代表任何类型的实例,包含函数类型(包括可选类型),协议
anyobject 可以代表任何类型的实例,对象类型
_objc_msgForward函数是做什么的,直接调用它将会发生什么?
_objc_msgForward是IMP类型,用于消息转发的:当一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward 会尝试做消息转发。
详解: _objc_msgForward 在进行消息转发的过程中会涉及以下这几个方法:
- resolveInstanceMethod: f方法(或 resolveClassMethod:)。
- forwardingTargetForSelector:方法
- methodSignatureForSelector:方法
- forwardInvocation:方法
- doesNotRecognizeSelector:方法
runtime如何实现weak变量的自动置为nil?知道SideTable吗?
runtime如何实现weak属性具体流程大致可以分为3步:
- weak的原理在于底层维护了一张weak_table_t结构的hash表,key是所指对象的地址,value是weak指针的地址数组。
- 初始化的时: runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址
- 添加引用时: objc_initWeak 函数会调用objc_storeWeak() 函数, objc_stroeWeak()的作用是更新指针指向,创建对应的弱引用表。
- weak 关键字的作用是弱引用,所引用对象的计数器不会加1,并在引用对象被释放的时候自动被设置为 nil。
- 对象释放时,调用clearDeallocating函数根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
SideTable结构体是负责管理类的引用计数和weak表。
weak 修饰的指针默认值是 nil (在Objective-C中向nil发送消息是安全的)。
15. Compile Error/ Runtime Crash/ NSLog... ?
下面的代码会? Compile Error/ Runtime Crash/ NSLog.. ?
@interface NSObject (Sark)
+ (void)foo;
- (void)foo;
@end
@implementation NSObject (Sark)
- (void)foo {
NSLog(@"IMP: -[NSObject (Sark) foo]");
}
@end
// 测试代码
[NSObject foo];
[[NSObject new] performSelector:@selector(foo)];
IMP: -[NSObject(Sark) foo] ,全都正常输出,编译和运行都没有问题。
这道题和上一到题很相似,第二个调用肯定没有问题,第一个调用后会从元类中查找方法,然而方法并不在元类中,所以找元类的superclass。方法定义是在NSObject的Category,由于NSObject的对象模型比较特殊,元类的superclass是类对象,所以从类对象中找到了对象并调用。
load和initialize的区别
load:
当类被引用进项目的时候就会执行load函数(在main函数开始执行之前),与这个类是否被用无关,每个类的load函数只会自动调用一次。由于load函数是系统自动加载的,因此不需要调用父类的load函数,否则父类的load函数会多次执行。
load 函数的执行顺序:
- 当父类和子类都实现load函数时,父类的load方法执行顺序要优于子类。
- 当子类未实现load方法时,不会调用父类的load方法。
- 类中的load方法执行顺序要优于类别
- 4.当有多个类别都实现了load方法,这几个load方法都会执行,但执行的顺序不确定(其执行顺序与类别在Compile Sources 中出现的顺序一致)
- 当然当有多个不同的类的时候,每个类的load执行顺序与其在Compile Sources 出现的顺序一致
load使用:
由于调用load方法时的环境很不安全,我们应该尽量减少load方法的逻辑。另一个原因是load方法是线程安全的,它的内部使用了锁,所以我们应该避免线程阻塞在load方法中。
load很常见的一个使用场景,交换两个方法的实现。
initialize:
在这个类接受第一条消息之前调用。即使类文件被引用进项目,但是没有使用,initialize不会被调用。由于这是系统调用,也不需要显式的调用父类的initialize,否则父类的initialize会被多次执行,假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。
initalize:
initialize initialize方法主要用来对一些不方便在编译期初始化的对象进行赋值。比如NSMutableArray这种类型的实例化依赖于runtime的消息发送,所以显然无法在编译器初始化:
- 当然当有多个不同的类的时候,每个类的load执行顺序与其在Compile Sources 出现的顺序一致
- 1.父类的initailize方法会比子类优先执行
- 2.当子类不实现initialize方法,会把父类的实现继承过来调用一遍
- 当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category的initalize方法)
参考链接
- 当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category的initalize方法)
什么情况下使用weak关键字,相比assign有什么不同?
- 在ARC中,在有可能出现循环引用的时候,往往要通过让其中的一端weak来解决
- assign 可以用于非OC对象,而weak必须用于OC对象
追加问题:如果用assign修饰一个OC对象会怎么样?
答:assign 和 weak 都是弱引用,对象的内存是存在堆上,而地址指针时存在于栈上的。对于assign修饰的基本数据类型,内存空间存在栈上面由系统统一管理。不需要程序员去管理,而存在于堆上的空间需要程序员去手动管理的,当我们将对象销毁的时候,对象的内存空间释放,存在于栈的指针也会nil,就不会产生野指针了。回到上面问题,如果assign修饰一个对象后,当对象被释放的时候,存在栈上的指针还是存在的。假如此时使用次指针,它就是一个野指针,就容易造成程序崩溃,如果用weak修饰的对象,则不会产生上面的情况,因为对象销毁的时候,系统会将指针置为nil,也就不会产生野指针了。
怎么用 copy 关键字?
1.NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
1.为啥要copy
下面代码示例
@property (nonatomic, copy) NSString *name;
NSMutableString *mtaString = [[NSMutableString alloc] initWithString:@"张三"];
self.name = mtaString;
[mtaString appendString: @"丰"];
NSLog(@"name=%@ mtaString: %@", self.name, mtaString);
输出是
name=张三丰 mtaString: 张三丰
这时候自己的name在自己没有修改情况因为赋值的NSMutableString的改变而修改,造成一些不必要的影响。
注意的是:下面的代码如果name赋值的NSMutableString则是深copy,如果是NSString这是浅copy。
- (void)setName:(NSString *)name
{
_name = [name copy];
}
附加问题 @property (nonatomic, copy) NSMutableArray *mutableArray; 这样用copy会有什么问题?
1、添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为 copy 就是复制一个不可变 NSArray 的对象
@property (nonatomic, copy) NSMutableArray *mutableArray;
NSMutableArray *array = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array;
[self.mutableArray removeObjectAtIndex:0];
接下来就会崩溃:
-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460
如何让自己的类用copy功能?
- 需声明该类遵从 NSCopying 协议
- 实现 NSCopying 协议。该协议只有一个方法:
- (id)copyWithZone:(NSZone *)zone;
Block相关?
1.使用block时什么情况会发生循环引用,如何解决?
一个对象中强引用了block,在block中又强引用了该对象,就会发生循环引用。
- 在block内如何修改block外部变量?
参考链接
apple 用什么方式实现对一个对象的KVO?如何手动触发kvo
Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象A时,KVO机制动态创建一个新的名为:NSKVONotifying_A 的新类,该类继承自对象A的本类,且 KVO 为 NSKVONotifying_A 重写观察属性的 setter 方法,setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况。
如何手动触发kvo?
// .m文件
// Created by https://github.com/ChenYilong
// 微博@iOS程序犭袁(http://weibo.com/luohanchenyilong/).
// 手动触发 value 的KVO,最后两行代码缺一不可。
//@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad {
[super viewDidLoad];
_now = [NSDate date];
[self addObserver:self forKeyPath:@"now" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"1");
[self willChangeValueForKey:@"now"]; // “手动触发self.now的KVO”,必写。
NSLog(@"2");
[self didChangeValueForKey:@"now"]; // “手动触发self.now的KVO”,必写。
NSLog(@"4");
}
autoreleasepool的原理和使用场景?
- 若干个autoreleasepoolpage组成的双向链表的栈结构, objc_autorelasepoolpush, objc_autoreleasepoolpop,objc_autorelease
- 使用场景:多次创建临时变量导致内存上涨时,需要延迟释放。
-
autoreleasepoolpage的内存结构:4k储存大小
9778944-e12b88657c490f33.jpg
网友评论