swift问题集--未完待续

达到效果:理解并能口诉才能算过

Q:dynamic 的作用-红记 静态 动态 kvo kvc 继承NSObject 

由于 swift 是一个静态语言, 所以没有 Objective-C 中的消息发送这些动态机制, dynamic 的作用就是让 swift 代码也能有 Objective-C 中的动态机制, 常用的地方就是 KVO 了, 如果要监控一个属性, 则必须要标记为 dynamic,首先需要知道的是, KVO, KVC 都是Objective-C 运行时的特性, Swift 是不具有的, 想要使用, 必须要继承 NSObject, 自然, 继承都没有的结构体也是没有 KVO, KVC 的, 这是前提.例如, 下面的这段错误代码:

class SimpleClass {
    var someValue: String = "123"
}
//SimpleClass().setValue("456", forKey: "someValue") // 错误, 必须要继承自 NSObject
 

KVC

Swift 下的 KVC 用起来很简单, 只要继承 NSObject 就行了.

class KVCClass :NSObject{
    var someValue: String = "123"
}
let kvc = KVCClass()
kvc.someValue // 123
kvc.setValue("456", forKey: "someValue")
kvc.someValue // 456

KVO

KVO 就稍微麻烦一些了,由于 Swift 为了效率, 默认禁用了动态派发, 因此想用 Swift 来实现 KVO, 除了继承NSObject我们还需要做额外的工作, 那就是将想要观测的对象标记为 dynamic.

class KVOClass:NSObject {

    dynamic var someValue: String = "123"

    var someOtherValue: String = "abc"

}

class ObserverClass: NSObject {

    func observer() {

        let kvo = KVOClass()

        kvo.addObserver(self, forKeyPath: "someValue", options: .new, context: nil)

        kvo.addObserver(self, forKeyPath: "someOtherValue", options: .new, context: nil)

        kvo.someValue = "456"

        kvo.someOtherValue = "def"

        kvo.removeObserver(self, forKeyPath: "someValue")

        kvo.removeObserver(self, forKeyPath: "someOtherValue")

    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        print("\(keyPath!) change to \(change![.newKey] as! String)")

    }

}

ObserverClass().observer()

这段代码只会输出someValue change to 456

Q:什么时候使用 @objc  感觉这不对 暴露 类 可选协议 方法 属性

@objc 用途是为了在 Objective-C 和 Swift 混编的时候, 能够正常调用 Swift 代码. 可以用于修饰类, 协议, 方法, 属性.
常用的地方是在定义 delegate 协议中, 会将协议中的部分方法声明为可选方法, 需要用到@objc

@objc protocol OptionalProtocol {
    @objc optional func optionalFunc()
    func normalFunc()
}
class OptionProtocolClass: OptionalProtocol {
    func normalFunc() {
    }
}
let someOptionalDelegate: OptionalProtocol = OptionProtocolClass()
someOptionalDelegate.optionalFunc?()

在 Swift 代码中,使用@objc修饰后的类型,可以直接供 Objective-C 调用。可以使用@objc修饰的类型包括:

  • 未嵌套的类
  • 协议
  • 非泛型枚举(仅限于原始值为整形的类型)
  • 类和协议中的属性和方法
  • 构造器和析构器
  • 下标

我们逐个来看看实际的使用:

@objc class MyHelper:NSObject {
    // class code
}

Objective-C 中所有的类都需要继承自NSObject,Swift 中的类需要供 Objective-C 调用的时候,自然也需要显式继承自NSObject。当然,你也可以继承所有 Objective-C 中的类,因为他们本身也继承自NSObject:

@objc class MyViewController : UIViewController {
    
}

另外一个细节是,Swift 中的类名,可以使用中文命名,而 Objective-C 中的却只能使用 ASCII 码,在使用@objc时,需要指定 Objective-C 中使用的 ASCII 名称。这个知识点请参见喵神的 tips

@objc(MyClass)
class 我的类: NSObject {
    @objc(greeting:)
    func 打招呼(名字: String) {
        print("哈喽,\(名字)")
    }
}

协议

@objc修饰协议与修饰类一样,需要注意的是,如果协议中有optional修饰的方法,就必须使用@objc来修饰:

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int { get }
}

关于可选协议的描述,可以参见官方教程

枚举

Swift 中的枚举类型,功能增强了不少。Objective-C 中还是传统的枚举类型,必须使用整型作为原始值。这样看来,Swift 中的枚举类型如果要被@objc修饰,则需要满足原始值是整型的限制条件。不然就会报编译错误。
关于如何在 Objective-C 中使用 Swift 枚举类型,可以见这个帖子:

// Swift
@objc enum Bear: Int {
    case Black, Grizzly, Polar
}

// OC 
Bear type = BearBlack;
switch (type) {
    case BearBlack:
    case BearGrizzly:
    case BearPolar:
       [self runLikeHell];
}

其他

在类和协议中的属性和方法,构造器和析构器,下标中使用@objc修饰的用法与上面的用法一样。这里举一个官网的例子说明:

@objc class ExampleClass: NSObject {
    var enabled: Bool {
        @objc(isEnabled) get {
            // Return the appropriate value
        }
    }
}

需要注意的是,如果类中方法或者属性被@objc修饰,那么类就必须被@objc修饰。

Q:Optional(可选型) 是用什么实现的  泛型枚举

Optional 是一个泛型枚举
大致定义如下:

enum Optional<Wrapped> {
  case none
  case some(Wrapped)
}

除了使用 let someValue: Int? = nil 之外, 还可以使用let optional1: Optional<Int> = nil 来定义

Q:如何自定义下标获取  实现subscript 给人的感觉很强迫

实现 subscript 即可, 如

//现在使用DailyMeal类是不是感觉到很简介,语义很明确也很健壮呢,答案是肯定的。我们通过下标避免向用户暴露不必要的API,同时也达到了高维护性的目的。DailyMeal类的完整代码如下:

/*

let monday = DailyMeal()

monday[.Lunch] = "Pizza"

print(monday[.Lunch])

//Output: "Pizza"

print(monday[.Dinner])

//Output: "Ramen"

*/

class DailyMeal{

    enum MealTime {

        case Breakfast

        case Lunch

        case Dinner

    }

    var meals: [MealTime : String] = [:]

    subscript(requestedMeal : MealTime) -> String {

        get {

            if let thisMeal = meals[requestedMeal] {

                return thisMeal

            } else {

                return "Ramen"

            }

        }

        set(newMealName) {

            meals[requestedMeal] = newMealName

        }

    }

}

//只读下标 何为只读下标,顾名思义就是不能通过下标赋值,只能通过下标查询。这种下标的应用场景一般是实现一些数据公式、数据函数,它们一般都是只需要你指定一个数字,然后返回该公式对该数字的计算结果。下面我们用一个阶乘的例子来说明只读下标:

struct FactorialGenerator{

    subscript(n: Int) -> Int {

        var result = 1

        if n > 0 {

            for value in 1...n {

                result *= value

            }

        }

        return result

    }

}

//可能已经注意到了,上面的下标并没有getter和setter方法。这是因为,如果你想定义一个只读的下标,那么可以不实现setter方法,并且可以省略getter方法的get关键字。Swfit的编译器会判断出这是一个只读的下标,如果你强行通过下标赋值,那么编译器会报错。

Q:?? 的作用

可选值的默认值, 当可选值为nil 的时候, 会返回后面的值. 如
let someValue = optional(1) ?? 0

Q:lazy 的作用

懒加载, 当属性要使用的时候, 才去完成初始化

class LazyClass {
    lazy var someLazyValue: Int = {
        print("lazy init value")
        return 1
    }()
    var someNormalValue: Int = {
        print("normal init value")
        return 2
    }()
}
let lazyInstance = LazyClass()
print(lazyInstance.someNormalValue)
print(lazyInstance.someLazyValue)
// 打印输出
// normal init value
// 2
// lazy init value
// 1

一个类型表示选项,可以同时表示有几个选项选中(类似 UIViewAnimationOptions ),用什么类型表示

需要实现自 OptionSet, 一般使用 struct 实现. 由于 OptionSet 要求有一个不可失败的init(rawValue:) 构造器, 而 枚举无法做到这一点(枚举的原始值构造器是可失败的, 而且有些组合值, 是没办法用一个枚举值表示的)

struct SomeOption: OptionSet {
    let rawValue: Int
    static let option1 = SomeOption(rawValue: 1 << 0)
    static let option2 =  SomeOption(rawValue:1 << 1)
    static let option3 =  SomeOption(rawValue:1 << 2)
}
let options: SomeOption = [.option1, .option2]

inout 的作用

输入输出参数, 如:

func swap( a: inout Int, b: inout Int) {
    let temp = a
    a = b
    b = temp
}
var a = 1
var b = 2
print(a, b)// 1 2
swap(a: &a, b: &b)
print(a, b)// 2 1

Error 如果要兼容 NSError 需要做什么操作

其实直接转换就可以, 例如 SomeError.someError as NSError 但是这样没有错误码, 描述等等, 如果想和 NSError 一样有这些东西, 只需要实现 LocalizedErrorCustomNSError 协议, 有些方法有默认实现, 可以略过, 如:

enum SomeError: Error, LocalizedError, CustomNSError {
    case error1, error2
    public var errorDescription: String? {
        switch self {
        case .error1:
            return "error description error1"
        case .error2:
            return "error description error2"
        }
    }
    var errorCode: Int {
        switch self {
        case .error1:
            return 1
        case .error2:
            return 2
        }
    }
    public static var errorDomain: String {
        return "error domain SomeError"
    }
    public var errorUserInfo: [String : Any] {
        switch self {
        case .error1:
            return ["info": "error1"]
        case .error2:
            return ["info": "error2"]
        }
    }
}
print(SomeError.error1 as NSError)
// Error Domain=error domain SomeError Code=1 "error description error1" UserInfo={info=error1}

下面的代码都用了哪些语法糖

[1, 2, 3].map{ $0 * 2 }
[1, 2, 3] 使用了, Array 实现的ExpressibleByArrayLiteral 协议, 用于接收数组的字面值
map{xxx} 使用了闭包作为作为最后一个参数时, 可以直接写在调用后面, 而且, 如果是唯一参数的话, 圆括号也可以省略
闭包没有声明函数参数, 返回值类型, 数量, 依靠的是闭包类型的自动推断
闭包中语句只有一句时, 自动将这一句的结果作为返回值
$0 在没有声明参数列表的时候, 第一个参数名称为$0, 后续参数以此类推

什么是高阶函数

一个函数如果可以以某一个函数作为参数, 或者是返回值, 那么这个函数就称之为高阶函数, 如 map, reduce, filter

如何解决引用循环

  1. 转换为值类型, 只有类会存在引用循环, 所以如果能不用类, 是可以解引用循环的,
  2. delegate 使用 weak 属性.
  3. 闭包中, 对有可能发生循环引用的对象, 使用 weak 或者 unowned, 修饰

下面的代码会不会崩溃,说出原因

var mutableArray = [1,2,3]
for _ in mutableArray {
  mutableArray.removeLast()
}

不会, 原理不清楚, 就算是把 removeLast(), 换成 removeAll() ,这个循环也会执行三次, 估计是在一开始, for
in 就对 mutableArray 进行了一次值捕获, 而 Array 是一个值类型 , removeLast() 并不能修改捕获的值.

给集合中元素是字符串的类型增加一个扩展方法,应该怎么声明

使用 where 子句, 限制 Element 为 String

extension Array where Element == String {
    var isStringElement:Bool {
        return true
    }
}
["1", "2"].isStringElement
//[1, 2].isStringElement// error

定义静态方法时关键字 static 和 class 有什么区别

static 定义的方法不可以被子类继承, class 则可以

class AnotherClass {
    static func staticMethod(){}
    class func classMethod(){}
}
class ChildOfAnotherClass: AnotherClass {
    override class func classMethod(){}
    //override static func staticMethod(){}// error
}

一个 Sequence 的索引是不是一定从 0 开始?

不一定, 两个 for in 并不能保证都是从 0 开始, 且输出结果一致, 官方文档如下

Repeated Access

The Sequence protocol makes no requirement on conforming types regarding
whether they will be destructively consumed by iteration. As a
consequence, don't assume that multiple for-in loops on a sequence
will either resume iteration or restart from the beginning:

for element in sequence {
    if ... some condition { break }
}

for element in sequence {
    // No defined behavior
}

有些同学还是不太理解, 我写了一个demo 当作参考

class Countdown: Sequence, IteratorProtocol {
    var count: Int
    init(count: Int) {
        self.count = count
    }
    func next() -> Int? {
       if count == 0 {
           return nil
       } else {
           defer { count -= 1 }
           return count
       }
   }
}

var countDown = Countdown(count: 5)
print("begin for in 1")
for c in countDown {
    print(c)
}
print("end for in 1")
print("begin for in 2")
for c in countDown {
    print(c)
}
print("end for in 2")

最后输出的结果是

begin for in 1
5
4
3
2
1
end for in 1
begin for in 2
end for in 2

很明显, 第二次没有输出任何结果, 原因就是在第二次for in 的时候, 并没有将count 重置.

数组都实现了哪些协议

MutableCollection, 实现了可修改的数组, 如 a[1] = 2
ExpressibleByArrayLiteral, 实现了数组可以从[1, 2, 3] 这种字面值初始化的能力
...

如何自定义模式匹配

这部分不太懂, 贴个链接吧
http://swifter.tips/pattern-match/

autoclosure 的作用

自动闭包, 会自动将某一个表达式封装为闭包. 如

func autoClosureFunction(_ closure: @autoclosure () -> Int) {
   closure()
}
autoClosureFunction(1)

详细可参考http://swifter.tips/autoclosure/

编译选项 whole module optmization 优化了什么

编译器可以跨文件优化编译代码, 不局限于一个文件.
http://www.jianshu.com/p/8dbf2bb05a1c

下面代码中 mutating 的作用是什么

struct Person {
  var name: String {
      mutating get {
          return store
      }
  }
}

让不可变对象无法访问 name 属性

如何让自定义对象支持字面量初始化

有几个协议, 分别是
ExpressibleByArrayLiteral 可以由数组形式初始化
ExpressibleByDictionaryLiteral 可以由字典形式初始化
ExpressibleByNilLiteral 可以由nil 值初始化
ExpressibleByIntegerLiteral 可以由整数值初始化
ExpressibleByFloatLiteral 可以由浮点数初始化
ExpressibleByBooleanLiteral 可以由布尔值初始化
ExpressibleByUnicodeScalarLiteral
ExpressibleByExtendedGraphemeClusterLiteral
ExpressibleByStringLiteral
这三种都是由字符串初始化, 上面两种包含有 Unicode 字符和特殊字符

dynamic framework 和 static framework 的区别是什么

静态库和动态库, 静态库是每一个程序单独打包一份, 而动态库则是多个程序之间共享

 

小礼物走一走,来简书关注我

赞赏支持



作者:yww
链接:https://www.jianshu.com/p/cc4a737ddc1d
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

1. class 和 struct 的区别

一个引用类型,一个值类型

2. 不通过继承,代码复用(共享)的方式有哪些

在swift 文件里直接写方法,相当于一个全局函数。
extension 给类直接扩展方法。

3. Set 独有的方法有哪些?

不会出现重复的值。
里面的元素必须时相同的类型。

4. 实现一个 min 函数,返回两个元素较小的元素

func min<T : Comparable>(_ a : T , b : T) -> T {
    return a < b ? a : b
}

5. map、filter、reduce 的作用

map : 映射 , 将一个元素根据某个函数 映射 成另一个元素(可以是同类型,也可以是不同类型)
filter : 过滤 , 将一个元素传入闭包中,如果返回的是false , 就过滤掉
reduce :先映射后融合(这样说容易理解) , 将数组中的所有元素映射融合在一起。举个例子:
求所有人的钱的总和,这里钱是字符串表示的

struct Person {
    let name : String
    let money : String
}
let a = Person(name: "小王", money: "5.0")
let b = Person(name: "小李", money: "7.1")
let c = Person(name: "小张", money: "3.22")

let sumMoney = [a , b , c].reduce(0) {
    return $0 + ($1.money as NSString).doubleValue
}
print(sumMoney)
//15.32

6. map 与 flatmap 的区别

map不能将元素映射成可选类型,flatmap可以

7. 什么是 copy on write

百度

8. 如何获取当前代码的函数名和行号

函数名#functio 行号#line 文件名#file

9. 如何声明一个只能被类 conform 的 protocol

protocol OnlyClassProtocol : class {

}

10. guard 使用场景

相当于

if !(...) {
  return
}

可以理解为拦截,凡是不满足 guard 后面条件的,都不会再执行下面的代码。
我一般用来解包 。 不能解包的, 就不能执行下面的代码

11. defer 使用场景

在一对花括号 : { } 里 使用defer ,这个defer 里面的内容将会在结束 {} 前(or 后?) 被执行

defer

11. String 与 NSString 的关系与区别

能够互相转换,一个值类型,一个引用类型

12. 怎么获取一个 String 的长度

print( "abcdefg".characters.count ) 
//7

("abcdefg" as NSString).length //这个要算作NSString的获取长度的方法

13. throws 和 rethrows 的用法与作用

throws 声明在函数的末尾,表示这个函数会抛出 Error的子类.
rethrows 再传入throws的闭包时,这个函数返回用rethrows

14. try? 和 try!是什么意思

try! 强制抛出错误,有错误就会崩溃
try? 抛出错误,没错误不会崩溃直接返回
try :

WX20170315-175706.png

如果不用do catch , 就会编译报错

WX20170315-175838.png

如果函数后面加上 throws , 将错误传递下去. 即使不用都do catch 编译也不会报错。 但是调用throwfun2()还是要使用do catch

14. associatedtype 的作用

相当于protocol的范型

protocol Animal {
    associatedtype AnimalType : Comparable
}

extension Animal {
    typealias AnimalType = Self
}

13. 什么时候使用 final

不允许class 被继承
不允许函数被重写

14. public 和 open 的区别

没用过 百度吧

15. 声明一个只有一个参数没有返回值闭包的别名

typealias NoReturn = (String) -> Void

16. Self 的使用场景

表示自己本身的类型,见14的例子

17. dynamic 的作用

动态化。用的少

18. 什么时候使用 @objc

与oc交互。

19. Optional(可选型) 是用什么实现的

enum 和 重载符号 ?

20. 如何自定义下标获取

WX20170315-182238.png

 

Array的文档

21. ?? 的作用

当 ?? 前面的值为nil 的时候就取 ??后面的值

22. lazy 的作用

只有属性被调用的时候才会执行lazy

WX20170315-183000.png

如果直接给属性赋值

WX20170315-183118.png

22. 一个类型表示选项,可以同时表示有几个选项选中(类似 UIViewAnimationOptions ),用什么类型表示

继承了OptionSet的struct , enum 。 class 没有试过,应该也可以

23. inout 的作用

改变函数外的值

24. Error 如果要兼容 NSError 需要做什么操作

不明白

25. 下面的代码都用了哪些语法糖 [1, 2, 3].map{ $0 * 2 }

用$0 捕获第一个参数。
只有一行代码的时候, 隐藏掉了 return 。

26. 什么是高阶函数

Curry化? 百度

27. 如何解决引用循环

weak unowned 关键字

28. 下面的代码会不会崩溃,说出原因

var mutableArray = [1,2,3]
for _ in mutableArray {
  mutableArray.removeLast()
}

在原题主的评论里有了

29. 给集合中元素是字符串的类型增加一个扩展方法,应该怎么声明

WX20170315-184548.png

 

参考文档的写法

WX20170315-184721.png

报错了。

 

WX20170315-185021.png

 

所以用了一个投机取巧的办法

30. 定义静态方法时关键字 static 和 class 有什么区别

struct 只能用static
class 的属性只能用 static , 其他地方都能随便用。
反正编译器会告诉你的

31. 一个 Sequence 的索引是不是一定从 0 开始?

不是。
ArraySlice是Sequence的子类,ArraySlice就不是

32. 数组都实现了哪些协议

翻文档??

33. 如何自定义模式匹配

不明白

34. autoclosure 的作用

可以传值或者有返回值的闭包。

35. 编译选项 whole module optmization 优化了什么

不知道

Q:下面代码中 mutating 的作用是什么

struct Person {

  var name: String {
      mutating get {
          return store
      }
  }
}

mutating 表示这个方法会(有可能)修改这个结构体, 只有var 的结构体才能调用mutation的方法

Q:如何让自定义对象支持字面量初始化

有几个协议, 分别是
ExpressibleByArrayLiteral 可以由数组形式初始化
ExpressibleByDictionaryLiteral 可以由字典形式初始化
ExpressibleByNilLiteral 可以由nil 值初始化
ExpressibleByIntegerLiteral 可以由整数值初始化
ExpressibleByFloatLiteral 可以由浮点数初始化
ExpressibleByBooleanLiteral 可以由布尔值初始化
ExpressibleByUnicodeScalarLiteral
ExpressibleByExtendedGraphemeClusterLiteral
ExpressibleByStringLiteral
这三种都是由字符串初始化, 上面两种包含有 Unicode 字符和特殊字符

Q:dynamic framework 和 static framework 的区别是什么

动态库和静态库,静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

Q:为什么数组索引越界会崩溃,而字典用下标取值时 key 没有对应值的话返回的是 nil 不会崩溃。

一款语言必须要有一些基础的可以完全信任的东西,否则就会陷入循环验证的悖论。所以第一题的答案就是,数组取下标被设计为可以完全信任的操作

Q:一个函数的参数类型只要是数字(Int、Float)都可以,要怎么表示。

func isNumber<T : SignedNumber>(_ number : T){
print("yes, it is a number")
}

4道过滤iOS菜鸟的面试题

1. struct和class的区别

swift中,class是引用类型,struct是值类型。值类型在传递和赋值时将进行复制,而引用类型则只会使用引用对象的一个"指向"。所以他们两者之间的区别就是两个类型的区别。

class有这几个功能struct没有的:

  • class可以继承,这样子类可以使用父类的特性和方法
  • 类型转换可以在runtime的时候检查和解释一个实例的类型
  • 可以用deinit来释放资源
  • 一个类可以被多次引用

struct也有这样几个优势:

  • 结构较小,适用于复制操作,相比于一个class的实例被多次引用更加安全。
  • 无须担心内存memory leak或者多线程冲突问题

顺便提一下,array在swift中是用struct实现的。Apple重写过一次array,然后复制就是深度拷贝了。猜测复制是类似参照那样,通过栈上指向堆上位置的指针来实现的。而对于它的复制操作,也是在相对空间较为宽裕的堆上来完成的,所以性能上还是不错的。

下面引用猫神OneV的博客:

var arr = [0,0,0]
var newArr = arr
arr[0] = 1
//Check arr and newArr
arr //[1, 0, 0]
newArr // before beta3:[1, 0, 0], after beta3:[0, 0, 0]

所以可以猜测其实在背后 Array和 Dictionary的行为并不是像其他 struct 那样简单的在栈上分配,而是类似参照那样,通过栈上指向堆上位置的指针来实现的。而对于它的复制操作,也是在相对空间较为宽裕的堆上来完成的。当然,现在还无法(或者说很难)拿到最后的汇编码,所以这只是一个猜测而已。

补充:
C语言中,struct与的class的区别:
struct只是作为一种复杂数据类型定义,不能用于面向对象编程。

C++中,struct和class的区别:
对于成员访问权限以及继承方式,class中默认的是private的,而struct中则是public的。class还可以用于表示模板类型,struct则不行。

2. 介绍一下观察者模式

观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
在IOS中典型的推模型实现方式为NSNotificationCenter和KVO。

NSNotificationCenter

  1. 观察者Observer,通过NSNotificationCenter的addObserver:selector:name:object接口来注册对某一类型通知感兴趣。在注册时候一定要注意,NSNotificationCenter不会对观察者进行引用计数+1的操作,我们在程序中释放观察者的时候,一定要去报从center中将其注销了。
  2. 通知中心NSNotificationCenter,通知的枢纽。
  3. 被观察的对象,通过postNotificationName:object:userInfo:发送某一类型通知,广播改变。
  4. 通知对象NSNotification,当有通知来的时候,Center会调用观察者注册的接口来广播通知,同时传递存储着更改内容的NSNotification对象。

KVO

KVO的全称是Key-Value Observer,即键值观察。是一种没有中心枢纽的观察者模式的实现方式。一个主题对象管理所有依赖于它的观察者对象,并且在自身状态发生改变的时候主动通知观察者对象。

  1. 注册观察者
    [object addObserver:self forKeyPath:property options:NSKeyValueObservingOptionNew context:]。
  2. 更改主题对象属性的值,即触发发送更改的通知。
  3. 在制定的回调函数中,处理收到的更改通知。

注销观察者 [object removeObserver:self forKeyPath:property]。

3.在一个HTTPS连接的网站里,输入账号密码点击登录后,到服务器返回这个请求前,中间经历了什么

这个非常得深非常得广,我来大概说一下。

HTTPS加密流程

  1. 客户端打包请求。包括url,端口啊,你的账号密码等等。账号密码登陆应该用的是Post方式,所以相关的用户信息会被加载到body里面。这个请求应该包含三个方面:网络地址,协议,资源路径。注意,这里是HTTPS,就是HTTP + SSL / TLS,在HTTP上又加了一层处理加密信息的模块(相当于是个锁)。这个过程相当于是客户端请求钥匙。

  2. 服务器接受请求。一般客户端的请求会先发送到DNS服务器。 DNS服务器负责将你的网络地址解析成IP地址,这个IP地址对应网上一台机器。这其中可能发生Hosts Hijack和ISP failure的问题。过了DNS这一关,信息就到了服务器端,此时客户端会和服务器的端口之间建立一个socket连接,socket一般都是以file descriptor的方式解析请求。这个过程相当于是服务器端分析是否要向客户端发送钥匙模板。

  3. 服务器端返回数字证书。服务器端会有一套数字证书(相当于是个钥匙模板),这个证书会先发送给客户端。这个过程相当于是服务器端向客户端发送钥匙模板。

  4. 客户端生成加密信息。根据收到的数字证书(钥匙模板),客户端会生成钥匙,并把内容锁上,此时信息已经加密。这个过程相当于客户端生成钥匙并锁上请求。

  5. 客户端发送加密信息。服务器端会收到由自己发送出去的数字证书加锁的信息。 这个时候生成的钥匙也一并被发送到服务器端。这个过程是相当于客户端发送请求。

  6. 服务器端解锁加密信息。服务器端收到加密信息后,会根据得到的钥匙进行解密,并把要返回的数据进行对称加密。这个过程相当于服务器端解锁请求、生成、加锁回应信息。

  7. 服务器端向客户端返回信息。客户端会收到相应的加密信息。这个过程相当于服务器端向客户端发送回应。

  8. 客户端解锁返回信息。客户端会用刚刚生成的钥匙进行解密,将内容显示在浏览器上。

HTTPS加密过程详解请去https原理:证书传递、验证和数据加密、解密过程解析

4.在一个app中间有一个button,在你手触摸屏幕点击后,到这个button收到点击事件,中间发生了什么

响应链大概有以下几个步骤

  1. 设备将touch到的UITouch和UIEvent对象打包, 放到当前活动的Application的事件队列中
  2. UIApplication会从事件队列中取出触摸事件并传递给单例UIWindow
  3. UIWindow使用hitTest:withEvent:方法查找touch操作的所在的视图view

RunLoop这边我大概讲一下

  1. 主线程的RunLoop被唤醒
  2. 通知Observer,处理Timer和Source 0
  3. Springboard接受touch event之后转给App进程中
  4. RunLoop处理Source 1,Source1 就会触发回调,并调用_UIApplicationHandleEventQueue() 进行应用内部的分发。
  5. RunLoop处理完毕进入睡眠,此前会释放旧的autorelease pool并新建一个autorelease pool

深挖请去深入理解RunLoop

UIResponder是UIView的父类,UIView是UIControl的父类。

1. 给一个数组,要求写一个函数,交换数组中的两个元素

  • 二X程序员:
    好简单啊,直接写出以下结果
func swap(_ nums: inout [Int], _ p: Int, _ q: Int) {
    let temp = nums[p]
    nums[p] = nums[q]
    nums[q] = temp 
}
  • 普通程序员:
    首先跟面试官沟通,是什么类型的数组?面试官会说,任意。普通程序员微微一笑,写出以下代码
func swap<T>(_ nums: inout [T], _ p: Int, _ q: Int) {
    let temp = nums[p]
    nums[p] = nums[q]
    nums[q] = temp 
}
  • 文艺程序员:
    与面试官沟通,是什么类型的数组?有什么其他要求和限制?面试官会说,这是一个Swift面试题。文艺程序员心领神会,于是写出以下答案
func swap<T>(_ nums: inout [T], _ p: Int, _ q: Int) {
    (nums[p], nums[q]) = (nums[q], nums[p])
}

同时对以上代码写上相应测试,检测各种边界情况,再确认无误后,才会说,这道题目我完成了。
这道题目看似简单,实际上考察了程序员的审题、交流、以及测试的意识。技术上考察了Swift的泛型和Tuple的性质。

2. 实现一个函数,输入是任一整数,输出要返回输入的整数 + 2

这道题很多人上来就这样写:

func addTwo(_ num: Int) -> Int {
    return num + 2
}

接下来面试官会说,那假如我要实现 + 4 呢?程序员想了一想,又定义了另一个方法:

func addFour(_ num: Int) -> Int {
    return num + 4
}

这时面试官会问,假如我要实现返回 + 6, + 8 的操作呢?能不能只定义一次方法呢?正确的写法是利用 Swift 的 Currying 特性:

func add(_ num: Int) -> (Int) -> Int {
  return { val in
    return num + val
  }
}

let addTwo = add(2), addFour = add(4), addSix = add(6), addEight = add(8)

3. 精简以下代码

func divide(dividend: Double?, by divisor: Double?) -> Double? { 
  if dividend == nil { 
    return nil 
  }  
  if divisor == nil { 
    return nil 
  } 
  if divisor == 0 { 
    return nil
  }  
  return dividend! / divisor!
}

这题考察的是 guard let 语句以及 optional chaining,最佳答案是

func divide(dividend: Double?, by divisor: Double?) -> Double? { 
    guard let dividend = dividend, let divisor = divisor, divisor != 0 else {
        return nil
    }
    
    return dividend / divisor
}

4. 以下函数会打印出什么?

var car = "Benz" 
let closure = { [car] in 
  print("I drive \(car)")
} 
car = "Tesla" 
closure()

因为 clousre 已经申明将 car 复制进去了([car]),此时clousre 里的 car 是个局部变量,不再与外面的 car有关,所以会打印出"I drive Benz"。
此时面试官微微一笑,将题目略作修改如下:

var car = "Benz" 
let closure = {
  print("I drive \(car)")
} 
car = "Tesla" 
closure()

此时 closure 没有申明复制拷贝 car,所以clousre 用的还是全局的 car 变量,此时将会打印出 "I drive Tesla"

5. 以下代码会打印出什么?

protocol Pizzeria { 
  func makePizza(_ ingredients: [String])
  func makeMargherita()
} 

extension Pizzeria { 
  func makeMargherita() { 
    return makePizza(["tomato", "mozzarella"]) 
  }
}

struct Lombardis: Pizzeria { 
  func makePizza(_ ingredients: [String]) { 
    print(ingredients)
  } 
  func makeMargherita() {
    return makePizza(["tomato", "basil", "mozzarella"]) 
  }
}

let lombardis1: Pizzeria = Lombardis()
let lombardis2: Lombardis = Lombardis() 
lombardis1.makeMargherita()
lombardis2.makeMargherita()

答案:打印出如下两行
["tomato", "basil", "mozzarella"]
["tomato", "basil", "mozzarella"]
在Lombardis的代码中,重写了makeMargherita的代码,所以永远调用的是Lombardis 中的 makeMargherita。
再进一步,我们把 protocol Pizzeria 中的 func makeMargherita() 删掉,代码变为

protocol Pizzeria {
  func makePizza(_ ingredients: [String])
}

extension Pizzeria {
  func makeMargherita() {
    return makePizza(["tomato", "mozzarella"])
  }
}

struct Lombardis: Pizzeria {
  func makePizza(_ ingredients: [String]) {
    print(ingredients)
  }
  func makeMargherita() {
    return makePizza(["tomato", "basil", "mozzarella"])
  }
}

let lombardis1: Pizzeria = Lombardis()
let lombardis2: Lombardis = Lombardis()
lombardis1.makeMargherita()
lombardis2.makeMargherita()

这时候打印出如下结果:
["tomato", "mozzarella"]
["tomato", "basil", "mozzarella"]
因为lombardis1 是 Pizzeria,而 makeMargherita() 有默认实现,这时候我们调用默认实现。

6. Swift 中定义常量和 Objective-C 中定义常量有什么区别?

一般人会觉得没有差别,因为写出来好像也确实没差别。
OC是这样定义常量的:

const int number = 0;

Swift 是这样定义常量的:

let number = 0

首先第一个区别,OC中用 const 来表示常量,而 Swift 中用 let 来判断是不是常量。
上面的区别更进一步说,OC中 const 表明的常量类型和数值是在 compilation time 时确定的;而 Swift 中 let 只是表明常量(只能赋值一次),其类型和值既可以是静态的,也可以是一个动态的计算方法,它们在 runtime 时确定的。
<br >

7. Swift 中 struct 和 class 什么区别?举个应用中的实例

struct 是值类型,class 是引用类型。
看过WWDC的人都知道,struct 是苹果推荐的,原因在于它在小数据模型传递和拷贝时比 class 要更安全,在多线程和网络请求时尤其好用。我们来看一个简单的例子:

class A {
  var val = 1
}

var a = A()
var b = a
b.val = 2

此时 a 的 val 也被改成了 2,因为 a 和 b 都是引用类型,本质上它们指向同一内存。解决这个问题的方法就是使用 struct:

struct A {
  var val = 1
}

var a = A()
var b = a
b.val = 2

此时 A 是struct,值类型,b 和 a 是不同的东西,改变 b 对于 a 没有影响。

8. Swift 到底是面向对象还是函数式的编程语言?

Swift 既是面向对象的,又是函数式的编程语言。
说 Swift 是 Object-oriented,是因为 Swift 支持类的封装、继承、和多态,从这点上来看与 Java 这类纯面向对象的语言几乎毫无差别。
说 Swift 是函数式编程语言,是因为 Swift 支持 map, reduce, filter, flatmap 这类去除中间状态、数学函数式的方法,更加强调运算结果而不是中间过程。



作者:故胤道长
链接:https://www.jianshu.com/p/07c9c6464f83
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

iOS 基础题

  1. 分类和扩展有什么区别?可以分别用来做什么?分类有哪些局限性?分类的结构体里面有哪些成员?
  2. 讲一下atomic的实现机制;为什么不能保证绝对的线程安全(最好可以结合场景来说)?
  3. 被weak修饰的对象在被释放的时候会发生什么?是如何实现的?知道sideTable么?里面的结构可以画出来么?
  4. 关联对象有什么应用,系统如何管理关联对象?其被释放的时候需要手动将所有的关联对象的指针置空么?
  5. KVO的底层实现?如何取消系统默认的KVO并手动触发(给KVO的触发设定条件:改变的值符合某个条件时再触发KVO)?
  6. Autoreleasepool所使用的数据结构是什么?AutoreleasePoolPage结构体了解么?
  7. 讲一下对象,类对象,元类,跟元类结构体的组成以及他们是如何相关联的?为什么对象方法没有保存的对象结构体里,而是保存在类对象的结构体里?
  8. class_ro_tclass_rw_t 的区别?
  9. iOS 中内省的几个方法?class方法和objc_getClass方法有什么区别?
  10. 在运行时创建类的方法objc_allocateClassPair的方法名尾部为什么是pair(成对的意思)?
  11. 一个int变量被__block修饰与否的区别?
  12. 为什么在block外部使用__weak修饰的同时需要在内部使用__strong修饰?
  13. RunLoop的作用是什么?它的内部工作机制了解么?(最好结合线程和内存管理来说)
  14. 哪些场景可以触发离屏渲染?(知道多少说多少)

iOS 实战题

  1. AppDelegate如何瘦身?
  2. 反射是什么?可以举出几个应用场景么?(知道多少说多少)
  3. 有哪些场景是NSOperation比GCD更容易实现的?(或是NSOperation优于GCD的几点,知道多少说多少)
  4. App 启动优化策略?最好结合启动流程来说(main()函数的执行前后都分别说一下,知道多少说多少)
  5. App 无痕埋点的思路了解么?你认为理想的无痕埋点系统应该具备哪些特点?(知道多少说多少)
  6. 你知道有哪些情况会导致app崩溃,分别可以用什么方法拦截并化解?(知道多少说多少)
  7. 你知道有哪些情况会导致app卡顿,分别可以用什么方法来避免?(知道多少说多少)

网络题

  1. App 网络层有哪些优化策略?
  2. TCP为什么要三次握手,四次挥手?
  3. 对称加密和非对称加密的区别?分别有哪些算法的实现?
  4. HTTPS的握手流程?为什么密钥的传递需要使用非对称加密?双向认证了解么?
  5. HTTPS是如何实现验证身份和验证完整性的?
  6. 如何用Charles抓HTTPS的包?其中原理和流程是什么?
  7. 什么是中间人攻击?如何避免?

计算机系统题

  1. 了解编译的过程么?分为哪几个步骤?
  2. 静态链接了解么?静态库和动态库的区别?
  3. 内存的几大区域,各自的职能分别是什么?
  4. static和const有什么区别?
  5. 了解内联函数么?
  6. 什么时候会出现死锁?如何避免?
  7. 说一说你对线程安全的理解?
  8. 列举你知道的线程同步策略?
  9. 有哪几种锁?各自的原理?它们之间的区别是什么?最好可以结合使用场景来说

设计模式题

  1. 除了单例,观察者设计模式以外,还知道哪些设计模式?分别介绍一下
  2. 最喜欢哪个设计模式?为什么?
  3. iOS SDK 里面有哪些设计模式的实践?
  4. **设计模式是为了解决什么问题的?
  5. **设计模式的成员构成以及工作机制是什么?
  6. **设计模式的优缺点是什么?

架构 & 设计题

  1. MVC和MVVM的区别?MVVM和MVP的区别?
  2. 面向对象的几个设计原则了解么?最好可以结合场景来说。
  3. 可以说几个重构的技巧么?你觉得重构适合什么时候来做?
  4. 你觉得框架和设计模式的区别是什么?
  5. 看过哪些第三方框架的源码,它们是怎么设计的?设计好的地方在哪里,不好的地方在哪里,如何改进?(这道题的后三个问题的难度已经很高了,如果不是太N的公司不建议深究)

数据结构&算法题

  1. 链表和数组的区别是什么?插入和查询的时间复杂度分别是多少?
  2. 哈希表是如何实现的?如何解决地址冲突?
  3. 排序题:冒泡排序,选择排序,插入排序,快速排序(二路,三路)能写出那些?
  4. 链表题:如何检测链表中是否有环?如何删除链表中等于某个值的所有节点?
  5. 数组题:如何在有序数组中找出和等于给定值的两个元素?如何合并两个有序的数组之后保持有序?
  6. 二叉树题:如何反转二叉树?如何验证两个二叉树是完全相等的?

喜欢出的和不喜欢出的题

不难看出,整套面试题中的iOS部分占比其实并不大(三分之一),因为笔者认为:

高级 iOS 开发 = 高级开发 + (高级) iOS 开发。

而其中高级开发的部分应该作为优先考核的内容,目的在于首先要验证面试者是否具备高级开发必备的基本素质。这部分知识的掌握程度会直接影响一个开发者的研究和设计能力,包括横向和纵向的。而笔者个人觉得后面的**(高级) iOS 开发**的部分仅仅考查的是面试者对于 iOS 本身的理解程度(API,系统,开发工具等等)。

在这套里面,笔者个人最喜欢的几道题是:

  1. iOS SDK 里面有哪些设计模式的实践?
  2. 说一说你对线程安全的理解?
  3. 你知道有哪些情况会导致app崩溃,分别可以用什么方法拦截并化解?
  4. 看过哪些第三方框架的源码,它们是怎么设计的?
  5. 可以说几个重构的技巧么?你觉得重构适合什么时候来做?
  1. 这道题一箭双雕,不仅考察了面试者对设计模式这种通用性知识的了解,还可以考察其对iOS SDK的熟悉和思考程度。这里可以简单提几个:单例:UIApplication;观察者模式:KVO;类簇:NSNumber;装饰者模式:分类;命令模式:NSInvocation;享元模式:UITableviewCell(UITableview的重用)。还有更多,有兴趣的读者可以看一下《Objective-C 编程之道》这本书,它介绍了很多在 iOS SDK中使用的设计模式。
  2. 这道题我看到网上有些答案是错的,说的大概的意思是“同一时刻只有一个线程访问”。但是如果按照这个定义的话,那么那些无法改变的常量就不算是线程安全的了,所以显然这类定义就是错的。所以说学东西要具备批判性思维,尤其是看博客的时候,很多情况需要自己想想,主动去认证,去思考。
  3. 导致app崩溃的原因有很多,比如向某个对象发送其无法响应的方法,数组越界,集合类中添加nil对象,string访问越界,KVO不合理的移除关联key(KVO导致的崩溃不仅仅这一种原因)等。而崩溃非常影响用户体验,所以笔者认为一名高级 iOS 开发应该具备避免这些崩溃的能力,起码至少也要知道这些容易导致崩溃的场景。
  4. 看一些优秀开源框架的代码,梳理实现思路和细节可以帮助我们提高在类似场景下设计系统的能力。其实道理很简单,小时候学习写作文的办法是什么?- 就是背诵课文而已啊。因为写作是一种输出,所以如果你没有好词好句的积累(输入),自然写不出辞藻丰富的文章。写代码也是一样的道理~
  5. 重构的能力是笔者非常看重的能力。其实笔者个人认为关于重构的技巧可以早早学习,在后面写代码的时候尽可能做到一步到位(如果在排期允许的情况下),而且也对设计代码方面能力的提高有帮助:怎样才能设计出一个低耦合,高内聚,易扩展,易修改的系统?有专门的一本书来介绍重构:《重构 改善既有代码的设计》。

上面说了笔者喜欢考察的问题,下面说一下笔者不喜欢考察的是哪些问题:

  1. 如何查询线上的崩溃?
  2. 了解发布流程么?几个证书的区别?
  3. 有没有做过支付/地图/分享?
  4. dysm文件是什么,有什么作用?

笔者不考察这类问题的原因有两个:

  1. 这类问题考查不了面试者作为一名程序员的基本素质,因为其考察的内容仅仅局限于iOS本身。
  2. 这类问题往往是“做过即知道”,更没办法量化能力。在实际开发中遇到了就做过了;就算没遇到,没做过,笔者也相信一名优秀的程序员在第一次也会高效地做好。

建议准备数据结构和算法题

在本文的最后说一下数据结构和算法题。

这类问题是比较大的公司喜欢考核的内容,也就是说大部分公司其实并不考(但是如果了解的话是会加分的)。但是笔者个人认为如果时间上允许,多少还是准备一些会比较好。除了应对面试,其实算法方面的学习会对编程能力的提高有帮助,这一点笔者自己深有体会:

笔者这次准备面试的过程中,在LeetCode上面刷了一些道题,其中链表,数组,二叉树的题加起来有30道左右,并把这些题放在了个人仓库里面:awesome-algorithm-question-solution。欢迎PR Swift,Java的算法题和答案~

在刷题和学习的过程中渐渐能够感觉到对代码的理解能力提高了很多,尤其是链表题可以强化对指针操作的理解,而且对执行条件的检查,边界问题的处理能力也提升了一些~

好了,这套题就分享到这里了,在文章后面也建议大家平时注意数据结构和算法方面的学习。和上一篇一样,这篇博客主观方面的内容还是多一些的,还是希望读者可以多多和我交流~

本篇已同步到个人博客:出一套高级 iOS 面试题


笔者在近期开通了个人公众号,主要分享编程,读书笔记,思考类的文章。

  • 编程类文章:包括笔者以前发布的精选技术文章,以及后续发布的技术文章(以原创为主),并且逐渐脱离 iOS 的内容,将侧重点会转移到提高编程能力的方向上。
  • 读书笔记类文章:分享编程类思考类心理类职场类书籍的读书笔记。
  • 思考类文章:分享笔者平时在技术上生活上的思考。

因为公众号每天发布的消息数有限制,所以到目前为止还没有将所有过去的精选文章都发布在公众号上,后续会逐步发布的。

而且因为各大博客平台的各种限制,后面还会在公众号上发布一些短小精干,以小见大的干货文章哦~

扫下方的公众号二维码并点击关注,期待与您的共同成长~


作者:J_Knight_
链接:https://juejin.im/post/5b56155e6fb9a04f8b78619b
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

Swift面试题,看这些就够了

iOS面试,可能大部分人认为是编程里面最简单的面试:

不用考算法(排序是什么鬼,我们有sort足以做一切),
不用考内存泄露(因为都在用ARC),
大部分功能网上都有demo(甚至都不用github上搜,都不用google,直接百度就搜出一坨坨),
大概问问做过的东西(其实是就是在说产品功能,是个人都会说好嘛),
然后看着价格给得起就可以了...

基本如果是招干活的,毕竟在大部分人眼里,iOS或者android都只是请求网络参数,画个界面而已。关键还是后台的接口和系统的架构来支撑整个系统的稳定和发展

∴ 可能很多基本都是按照经验和背景来定价要人

对于这种观点,我第一反应是 :

"滚... 你以为你以为就是你以为的?"

"我们除了会画界面,我们还会cocoapods呢,
我们还会tableView呢,我们还会UIAlert提示错误呢?...哼~"(双手叉腰状...)

...

?
哈哈,估计我上面这句话一说,基本要被同行打死--

“别人说也就算了,你自己还是说出这么没水平的话”(围起来继续打...)

|

okey,言归正传..

iOS面试真的这么简单么?

我觉得一个公司面试iOS(当然android也同理)的水平和态度,间接反应了其产品的层次高低 以及 老板对技术/产品的侧重方向
(当然,我相信大部分公司面试都是很给力的,面试官也是很靠谱的,HR也是很清晰的...?)

iOS能问哪些问题?

1.技术方向

1.1 基本功

个人认为面试iOS != 只面试iOS

所以有的公司会上来先面试下计算机的基本功,也算是对这个人的全面了解:

我自己遇到过,上来写排序或者回文 + 反转链表的,有人会说,这不是很简单么,只有摸过编程书的,前10页肯定能找到这种题目。
当然是没错,但是就这种题,估计能吓跑一大半的面试者。

  1. 的确很多人基础不牢固
  2. 好久不用忘记了
    工作中, 其实这种最基础的算法类的东西,别说iOS,大部分语言都是不会用到的,因为已经基本集成到语言里面了,除非C/C++这种,所以,基本就是考察现场思考的水平了.

有人总结他是这么面试的,大家可以参照下:

1.通用编程技能,例如:一道小算法,数据结构的实现方式,网络,多线程。
2.开发语言,例如:语言特性,重新实现语言提供的功能,是否深入研究过这门语言的某部分。
3.开发平台,例如:该平台的内部消息,内存,线程等机制。
4.工具,例如:调试技巧,是否熟练使用,代码管理工具,项目管理工具,效率工具。
5.行业视角,例如:用什么,知道什么。
6.其他能力,例如:网络上解决问题的能力,是否有持续学习的意识。

有经验的,1-6都会问到,刚毕业的,只要重点面1和6

作者:焕德
链接:https://www.zhihu.com/question/19604641/answer/15657048
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1.2 真正的iOS题目

之前看过一个很火的文章,是百度的孙童鞋写的,我另一篇runloop的文章里面也提到过,如下

链接:招聘一个靠谱的iOS

如果你还没看过,那你赶紧看一下,然后也就不用再看我这篇文章了,
那个里面全是干货,当然,里面只有问题
答案虽然也能google的出来,但是建议还是自己多想想,
毕竟,面试和考试还是有一些区别的,自己的思考过程不仅记忆更深刻,而且也更能真正的提升自己嘛

总结过些面试题,没坚持下去,后来把这些当 checklist,面试的时候实在没话聊的时候做个提醒,语言、框架、运行机制性质的:

[※]@property中有哪些属性关键字?
[※]weak属性需要在dealloc中置nil么?
[※※]@synthesize和@dynamic分别有什么作用?
[※※※]ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
[※※※]用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
[※※※]@synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?
[※※※※※]在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?

[※※]objc中向一个nil对象发送消息将会发生什么?
[※※※]objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?
[※※※]什么时候会报unrecognized selector的异常?
[※※※※]一个objc对象如何进行内存布局?(考虑有父类的情况)
[※※※※]一个objc对象的isa的指针指向什么?有什么作用?
[※※※※]下面的代码输出什么?

上面的问题,我觉得足以刷掉一大半面试者(当然也包括我自己,嘿嘿)

难度再提升一些,如下:

[※※※※]runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)
[※※※※]使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?
[※※※※※]objc中的类方法和实例方法有什么本质区别和联系?
[※※※※※]_objc_msgForward函数是做什么的,直接调用它将会发生什么?
[※※※※※]runtime如何实现weak变量的自动置nil?
[※※※※※]能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

[※※※]runloop和线程有什么关系?
[※※※]runloop的mode作用是什么?
[※※※※]以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?
[※※※※※]猜想runloop内部是如何实现的?

[※]objc使用什么机制管理对象内存?
[※※※※]ARC通过什么方式帮助开发者管理内存?
[※※※※]不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)
[※※※※]BAD_ACCESS在什么情况下出现?
[※※※※※]苹果是如何实现autoreleasepool的?

[※※]使用block时什么情况会发生引用循环,如何解决?
[※※]在block内如何修改block外部变量?
[※※※]使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?

都答上来了?好那继续,还有更好玩的

[※※]GCD的队列(dispatch_queue_t)分哪两种类型?
[※※※※]如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)
[※※※※]dispatch_barrier_async的作用是什么?
[※※※※※]苹果为什么要废弃dispatch_get_current_queue?

1.3 当我们谈论iOS面试时,知乎都在讨论什么

知乎:如何面试 iOS 工程师
做部分摘抄:
来自自携程的某个童鞋的问题:

1. 什么是arc?(arc是为了解决什么问题诞生的?)
2. 请解释以下keywords的区别: assign vs weak, __block vs __weak
3. __block在arc和非arc下含义一样吗?
4. 使用atomic一定是线程安全的吗?
5. 描述一个你遇到过的retain cycle例子。(别撒谎,你肯定遇到过)
6. +(void)load; +(void)initialize;有什么用处?
7. 为什么其他语言里叫函数调用, objective c里则是给对象发消息(或者谈下对runtime的理解)
8. 什么是method swizzling?
9. UIView和CALayer是啥关系?
10. 如何高性能的给UIImageView加个圆角?(不准说layer.cornerRadius!)
11. 使用drawRect有什么影响?(这个可深可浅,你至少得用过。。)
12. ASIHttpRequest或者SDWebImage里面给UIImageView加载图片的逻辑是什么样的?(把UIImageView放到UITableViewCell里面问更赞)
13. 麻烦你设计个简单的图片内存缓存器(移除策略是一定要说的)
14. 讲讲你用Instrument优化动画性能的经历吧(别问我什么是Instrument)
15. loadView是干嘛用的?
16. viewWillLayoutSubView你总是知道的。。
17. GCD里面有哪几种Queue?你自己建立过串行queue吗?背后的线程模型是什么样的?
18. 用过coredata或者sqlite吗?读写是分线程的吗?遇到过死锁没?咋解决的?
19. http的post和get啥区别?(区别挺多的,麻烦多说点)
20. 我知道你大学毕业过后就没接触过算法数据结构了,但是请你一定告诉我什么是Binary search tree? search的时间复杂度是多少?我很想知道!

作者:高峰
链接:https://www.zhihu.com/question/19604641/answer/44151044
来源:知乎

最后是几道场景题,也是我最喜欢问的:
发送10个网络请求,然后再接收到所有回应之后执行后续操作,如何实现? 

实现一个第三方控件,可以在任何时候出现在APP界面最上层 

实现一个最简单的点击拖拽功能。

上面那个拖拽之外,如果在手放开时,需要根据速度往前滑动呢? 

如何减小一个应用程序的尺寸? 

如何提高一个性用程序的性能? 

不同版本的APP,数据库结构变化了,如何处理? 


作者:张之诚
链接:https://www.zhihu.com/question/19604641/answer/56306604
来源:知乎

2. 产品/设计方面的提问

iOS开发者相比于server端开发的童鞋,最大的优势是每天跟产品经理,UI设计师,交互设计师扯皮的时间最长,所以对产品的理解和想法也应该是最多的

很多时候,当产品提出需求的时候,一个好的开发者应该是在合理的范围内,提出一定自己的想法以及可能存在的问题。 很多时候,产品经理也是想一出是一出,如果开发者这么任他性子,他说什么就做什么的话,并不是一个真正靠谱的开发者(当然我也不是说每次都质疑需求,我只是说真正的和产品经理去讨论,怎样更合理更优化)。

除了基本的程序开发技巧外,会额外的让他谈谈当前热门的app的一些优劣。
答不出来的人大多只将开发app当作一份糊口的工作,不会用心做; 
另外可能是由于就职门户网站的缘故,我觉得iOS开发从某种程度上和前端开发很像,
因此如果会熟练使用Ps软件的开发(不要求设计,至少要会切图什么的),多半会有加分,
正如前面的朋友所说“认为漂亮是基本的要求”,一个人不会一些美术工具,怎么能做出漂亮的界面呢。

作者:王聪
链接:https://www.zhihu.com/question/19604641/answer/12358486
来源:知乎

|

还有关于设计,有人列举了这么一些问题:

原始链接在这里:http://www.csdn.net/article/2015-01-19/2823604-ios-interview-questions

关于设计

iOS应用图标是指什么?请尽可能详细地描述一下。
最小尺寸和最大尺寸的应用图标分别是什么样子的?
应用图标能否包含透明的部分?
Newsstand的图标与常规应用有何不同?
请解释一下启动画面(Launch Images)。
自动布局(Auto Layout)的作用是什么?请概括一下它是如何运行的。
设计软件时为什么要加上动画?
请描述一下软件设计中的交互和Feedback有什么作用。
设计iPhone和iPad应用时,应分别考虑哪些因素?
请描述一下原型设计对于软件开发的意义。其作用是什么?
关于App Store

应用内购买(In-App Purchases)是怎么回事?IAP能够为用户带来哪些新体验?
你是否在App Store上发布过应用?能否概括一下过程?
iTunes Connect是什么?
Provisioning Profiles是指?
App ID是什么?
iOS的开发和发布签名证书有何异同?
如何使用TestFlight?通过Ad-hoc发布应用的话,该如何使用UUID?
应何时验证购买收据?
发布iAds(苹果平台广告)有哪些要求?

3.有木有面经分享

当然有了

3.1 :百度移动云可穿戴部门的面试经历

来自:不会开机的男孩

polen:这个是某个童鞋百度的面试经验(时间可能比较早了)

3.2 让 BAT 的 Offer 不再难拿

文/bestswifter(简书作者)
原文链接:http://www.jianshu.com/p/ee15c1cf9c16

polen: 这哥们面了多家,最终去百度

3.3 2016年1月TX电面题

polen: 腾讯的题目回忆

|

挑几个不错的,列举下:

百度


一面:约 1.5 小时
首先是四个算法题:

不用临时变量怎么实现 swap(a, b)——用加法或者异或都可以
二维有序数组查找数字——剑指 offer 第 3题
亿级日志中,查找登陆次数最多的十个用户——(不确定对不对,我的思路是)先用哈希表保存登陆次数和ID,然后用红黑树保存最大的十个数。剑指 offer 第 30题
简述排序算法——快排,partion 函数的原理,堆排(不稳定),归并排序,基数排序。

说说你对 OC 中 load 方法和 initialize 方法的异同。——主要说一下执行时间,各自用途,没实现子类的方法会不会调用父类的?
说说你对 block 的理解。—— 三种 block,栈上的自动复制到堆上,block 的属性修饰符是 copy,循环引用的原理和解决方案。
说说你对 runtime 的理解。——主要是方法调用时如何查找缓存,如何找到方法,找不到方法时怎么转发,对象的内存布局。
说说你对 MVC 和 MVVM 的理解。—— MVC 的 C 太臃肿,可以和 V 合并,变成 MVVM 中的 V,而 VM 用来将 M 转化成 V 能用的数据。
说说 UITableView 的调优。——一方面是通过 instruments 检查影响性能的地方,另一方面是估算高度并在 runloop 空闲时缓存。
谈谈你对 ARC 的理解。ARC 是编译器完成的,依靠引用计数,谈谈几个属性修饰符的内存管理策略,什么情况下会内存泄露。

阿里

阿里一面:
MVC 具有什么样的优势,各个模块之间怎么通信,比如点击 Button 后 怎么通知 Model?
两个无限长度链表(也就是可能有环) 判断有没有交点
UITableView 的相关优化
KVO、Notification、delegate 各自的优缺点,效率还有使用场景
如何手动通知 KVO
Objective-C 中的 copy 方法
runtime 中,SEL 和 IMP 的区别
autoreleasepool 的使用场景和原理
RunLoop 的实现原理和数据结构,什么时候会用到
block 为什么会有循环引用
使用 GCD 如何实现这个需求:A、B、C 三个任务并发,完成后执行任务 D。
NSOperation 和 GCD 的区别
CoreData 的使用,如何处理多线程问题
如何设计图片缓存?
有没有自己设计过网络控件?
阿里二面:
怎么判断某个 cell 是否显示在屏幕上
进程和线程的区别
TCP 与 UDP 区别
TCP 流量控制
数组和链表的区别
UIView 生命周期
如果页面 A 跳转到 页面 B,A 的 viewDidDisappear 方法和 B 的 viewDidAppear 方法哪个先调用?
block 循环引用问题
ARC 的本质
RunLoop 的基本概念,它是怎么休眠的?
Autoreleasepool 什么时候释放,在什么场景下使用?
如何找到字符串中第一个不重复的字符
哈希表如何处理冲突

腾讯

如何绘制一个三角形? 
1.1 如何绘制大量三角形? 
1.2 一定要重写drawRect吗? 
1.3 如何刷新View界面? 
1.4 Layer好在哪?
assign和weak的区别
线程和RunLoop的关系 
3.1 一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)
NSTimer有什么需注意的以及和RunLoop的关系?
NSString copy 和 NSString mutableCopy 的区别
线程加锁原理(信号量,临界区,自选锁)
iOS7 - iOS9的区别
GCD指向了野指针了怎么办
用HTTP传数据,丢包严重怎么办
iOS中广播的种类
app slying
runtime如何实现weak变量的自动置nil?
AFNetworking的内部实现原理?
block循环引用了如何解决?
如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)
UIKit的框架结构?

4.人的方面

除了产品和技术,剩下的就是看人了,那怎么看人呢?
有句老话说的好:

首先,如果是妹子加10分,颜值高的double,身材再好的,直接招了吧,不用往下看了...

@end



作者:pingpong_龘
链接:https://www.jianshu.com/p/530939374c10
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

 

2017年03月03日 15:06:15 kidd风 阅读数 7830 标签: swift面试swiftTipsiOS面试题 更多

个人分类: IOS开发日志

最近看到一篇写swift面试题的文章,写的非常好,特此翻译过来供大家查阅,每个问题都会提供答案。

 

书面问题

初学者

Question #1

请用更好的方式写这个for循环:

 

for var i = 0; i < 5; i++ {
  print("Hello!")
}

 

 

Answer:

 

for _ in 0...4 {
  print("Hello!")
}

Swift实现了两个范围操作符,闭合操作符和半开操作符。 第一个包括范围中的所有值。 例如,以下包括从0到4的所有整数:
0 ... 4
半开操作符不包括最后一个元素。 以下产生相同的0到4结果:
0 .. <5

 

 

 

Question #2

考虑下面的代码:

 

struct Tutorial {
  var difficulty: Int = 1
}
 
var tutorial1 = Tutorial()
var tutorial2 = tutorial1
tutorial2.difficulty = 2

 

tutorial1.difficulty和tutorial2.difficulty的值是什么? 如果Tutorial是一个类,这将是什么不同? 为什么?

 

 

Answer:

tutorial1.difficulty是1,而tutorial2.difficulty是2。
Swift中的结构是值类型,它们通过值而不是引用来复制。 以下行创建tutorial1的copy并将其分配给tutorial2:

 
var tutorial2 = tutorial1
 
从这一行开始,对tutorial2的任何更改都不会反映在tutorial1中。
如果Tutorial是一个类,tutorial1.difficulty和tutorial2.difficulty将是2.在Swift中的类是引用类型。 对tutorial1的属性的任何更改都将反映到tutorial2中,反之亦然。

 

 

 

Question #3

view1用var声明,view2用let声明。 这里有什么区别,最后一行会编译?

 

import UIKit
 
var view1 = UIView()
view1.alpha = 0.5
 
let view2 = UIView()
view2.alpha = 0.5 // Will this line compile?

 

 

 

Answer:

view1是一个变量,可以重新分配给一个新的UIView实例。 通过让你只能赋值一次,所以下面的代码不编译:

 

view2 = view1 // Error: view2 is immutable
但是,UIView是一个具有引用语义的类,所以你可以改变view2的属性(这意味着最后一行将编译):
 
let view2 = UIView()
view2.alpha = 0.5 // Yes!

 

 

 

Question #4

此代码按字母顺序对名称数组进行排序,看起来很复杂。 尽可能简化它和关闭。

 

let animals = ["fish", "cat", "chicken", "dog"]
let sortedAnimals = animals.sort { (one: String, two: String) -> Bool in
  return one < two
}

 

 

 

Answer:

第一个简化与参数有关。类型推理系统可以计算闭包中的参数的类型,所以你可以摆脱它们:

let sortedAnimals = animals.sort { (one, two) -> Bool in return one < two }

返回类型也可以推断,所以放弃它:

let sortedAnimals = animals.sort { (one, two) in return one < two }

$ i表示法可以替换参数名称:

 

let sortedAnimals = animals.sort { return $0 < $1 }

在单语句闭包中,可以省略return关键字。最后一条语句的返回值成为闭包的返回值:

let sortedAnimals = animals.sort { $0 < $1 }

这已经更简单,但现在不要停止!
对于字符串,有一个比较函数定义如下:

func <(lhs: String, rhs: String) -> Bool

这个整洁的小函数使你的代码像下面这样容易:

let sortedAnimals = animals.sort(<)

请注意,此渐进的每个步骤都会编译并输出相同的结果,并且您创建了一个字符闭包!

 

 

 

Question #5

此代码创建两个类,Address和Person,它创建两个实例来表示Ray和Brian。

 

class Address {
  var fullAddress: String
  var city: String
 
  init(fullAddress: String, city: String) {
    self.fullAddress = fullAddress
    self.city = city
  }
}
 
class Person {
  var name: String
  var address: Address
 
  init(name: String, address: Address) {
    self.name = name
    self.address = address
  }
}
 
var headquarters = Address(fullAddress: "123 Tutorial Street", city: "Appletown")
var ray = Person(name: "Ray", address: headquarters)
var brian = Person(name: "Brian", address: headquarters)

假设Brian移动到街对面的新建筑,所以你更新代码如下:

 

 

brian.address.fullAddress = "148 Tutorial Street"

 

 

问题是,这样做有什么问题吗?

 

 

Answer:

ray也搬到了新大楼! Address是一个类,有引用语义,所以headquarters是相同的实例,无论你通过ray或brian访问它。 改变headquarters的地址将改变它们。 你能想象如果brian得到ray的邮件会发生什么,反之亦然? 
解决方案是创建一个新的地址分配给Brian,或者将Address声明为结构体而不是类。

 

 

中场:

现在要加大难度了。 你准备好了吗?

 

Question #1

考虑下面的代码:

 

var optional1: String? = nil
var optional2: String? = .None

 

nil和.None之间有什么区别? optional1和optional2变量如何不同?

 

 

Answer:

没有什么区别。 Optional 、None(.None)是初始化缺少值的可选变量的正确方法,而nil仅仅是.None的语法糖。
事实上,参考下面代码:

 

nil == .None // On Swift 1.x this doesn't compile. You need Optional<Int>.None

 

记住,在hood下可选是一个枚举

 

enum Optional<T> {
  case None
  case Some(T)
}

 

 

 

Question #2

这里有一个Thermometer(温度计)的模型作为类和结构:

 

public class ThermometerClass {
  private(set) var temperature: Double = 0.0
  public func registerTemperature(temperature: Double) {
    self.temperature = temperature
  }
}
 
let thermometerClass = ThermometerClass()
thermometerClass.registerTemperature(56.0)
 
public struct ThermometerStruct {
  private(set) var temperature: Double = 0.0
  public mutating func registerTemperature(temperature: Double) {
    self.temperature = temperature
  }
}
 
let thermometerStruct = ThermometerStruct()
thermometerStruct.registerTemperature(56.0)

 

这段代码错在哪里?为什么会错?

 

 

Answer:

编译器会在最后一行报错。 ThermometerStruct被正确地声明了一个变化函数来改变其内部变量的温度,但编译器报错,因为registerTemperature被调用通过let创建的实例,因此它是不可变的。
在结构中,更改内部状态的方法必须标记为突变,但不允许从不可变变量调用它们。

 

 

Question #3 

下面这段代码会打印出什么?为什么?

 

var thing = "cars"
 
let closure = { [thing] in
  print("I love \(thing)")
}
 
thing = "airplanes"
 
closure()

 

 

 

 

Answer:

它会打印I love cars。 捕获列表在声明闭包时创建事物的副本,因此捕获的值不会更改,即使您为thing分配了一个新值。
如果省略闭包中的捕获列表,编译器将使用引用而不是副本。 在这种情况下,当调用闭包时,会反映对变量的任何更改,如下面的代码所示:

 

var thing = "cars"
 
let closure = {    
  print("I love \(thing)")
}
 
thing = "airplanes"
 
closure() // Prints "I love airplanes"

 

 

 

Question #4

这是一个全局函数,用于计算数组中唯一值的数量:

 

func countUniques<T: Comparable>(array: Array<T>) -> Int {
  let sorted = array.sort(<)
  let initial: (T?, Int) = (.None, 0)
  let reduced = sorted.reduce(initial) { ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) }
  return reduced.1
}

 

它使用<和==运算符,因此它将T限制为实现可比较协议的类型。
你可以这样使用它:

 

countUniques([1, 2, 3, 3]) // result is 3

 

将这个函数重写为Array上的扩展方法,实现以下调用:

 

[1, 2, 3, 3].countUniques() // should print 3

 

 

 

 

Answer:

在Swift 2.0中,可以通过强制类型约束使用条件扩展泛型类型。 如果通用类型不满足约束,则扩展既不可见也不可访问。
因此,全局countUniques函数可以重写为Array扩展:

 

extension Array where Element: Comparable {
  func countUniques() -> Int {
    let sorted = sort(<)
    let initial: (Element?, Int) = (.None, 0)
    let reduced = sorted.reduce(initial) { ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) }
    return reduced.1
  }
}

注意,只有当通用Element类型实现了Comparable协议时,新方法才可用。 例如,如果你在UIViews数组上调用它,编译器会报错,如下所示:

 

 

import UIKit
let a = [UIView(), UIView()]
a.countUniques() // compiler error here because UIView doesn't implement Comparable

 

 

 

 

Question #5

这里有一个函数来计算给予两个(可选)双精度的除法。 在执行实际划分之前有三个前提条件要验证:
- 被除数必须包含非零值
- 除数必须包含非零值
- 除数不能为零

 

func divide(dividend: Double?, by divisor: Double?) -> Double? {
  if dividend == .None {
    return .None
  }
 
  if divisor == .None {
    return .None
  }
 
  if divisor == 0 {
    return .None
  }
 
  return dividend! / divisor!
}

此代码按预期工作,但有两个问题:
前提条件可以利用guard语句
它使用强制解包
使用guard语句改进此函数,并避免使用强制解包。

 

 

 

Answer:

 

 
func divide(dividend: Double?, by divisor: Double?) -> Double? {
  guard let dividend = dividend, divisor = divisor where divisor != 0 else { return .None }
  return dividend / divisor
}

 

 

 

 

高级

Question #1

考虑下面的代码:

public struct Thermometer {
  public var temperature: Double
  public init(temperature: Double) {
    self.temperature = temperature
  }
}

首先,创建一个实例:

var t: Thermometer = Thermometer(temperature:56.8)

但是这样更好地初始化它:

var thermometer: Thermometer = 56.8

 

 

问题:如何实现它?

 

 

Answer:

Swift定义了以下协议,通过使用赋值运算符,可以使用文字值初始化类型:

 

  • NilLiteralConvertible
  • BooleanLiteralConvertible
  • IntegerLiteralConvertible
  • FloatLiteralConvertible
  • UnicodeScalarLiteralConvertible
  • ExtendedGraphemeClusterLiteralConvertible
  • StringLiteralConvertible
  • ArrayLiteralConvertible
  • DictionaryLiteralConvertible

采用相应的协议并提供公共初始化器允许特定类型的文本初始化。 在温度计类型的情况下,您实现FloatLiteralConvertible协议如下:

 

 

extension Thermometer : FloatLiteralConvertible {
  public init(floatLiteral value: FloatLiteralType) {
    self.init(temperature: value)
  }
}

 

 

 

 

Question #2 

Swift有一组预定义的操作符,执行不同类型的操作,如算术或逻辑。 它还允许创建自定义运算符,一元或二进制。
定义并实现具有以下规格的定制^^电源操作员:
- 取两个Ints作为参数
- 返回第二个参数的第一个参数
- 忽略潜在的溢出错误

 

 

Answer:

在两个步骤中创建新的自定义运算符:声明和实现。
声明使用operator关键字来指定类型(一元或二进制),组成运算符的字符序列,其关联性和优先级。
在这种情况下,运算符为^^,类型为中缀(二进制)。 关联性是正确的,优先级设置为155,因为乘法和除法的值为150.这里是声明:

 

infix operator ^^ { associativity right precedence 155 }

实现如下:

 

 

func ^^(lhs: Int, rhs: Int) -> Int {
  let l = Double(lhs)
  let r = Double(rhs)
  let p = pow(l, r)
  return Int(p)
}

注意,它不考虑溢出; 如果操作产生Int不能表示的结果,例如。 大于Int.max,则发生运行时错误。

 

 

 

Question #3

你能用这样的原始值定义一个枚举吗? 为什么?

 

enum Edges : (Double, Double) {
  case TopLeft = (0.0, 0.0)
  case TopRight = (1.0, 0.0)
  case BottomLeft = (0.0, 1.0)
  case BottomRight = (1.0, 1.0)
}

 

 

 

 

Answer:

不能。 原始值类型必须:
- 符合Equatable协议
- 可以是以下任何类型的字面转换:
Int
String
Character
在上面的代码中,原始类型是一个元组,并且不兼容 - 即使它的各个值。

 

 

Question #4

考虑下面的代码,将Pizza定义为结构体,Pizzeria定义为协议,扩展名包含方法makeMargherita()的默认实现:

 

struct Pizza {
  let ingredients: [String]
}
 
protocol Pizzeria {
  func makePizza(ingredients: [String]) -> Pizza
  func makeMargherita() -> Pizza
}
 
extension Pizzeria {
  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "mozzarella"])
  }
}

现在创建一个Lombardis的结构体:

 

 

struct Lombardis: Pizzeria {
  func makePizza(ingredients: [String]) -> Pizza {
    return Pizza(ingredients: ingredients)
  }
  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "basil", "mozzarella"])
  }
}

以下代码创建Lombardi的两个实例。 这两个中的哪一个将包含basil?

 

 

let lombardis1: Pizzeria = Lombardis()
let lombardis2: Lombardis = Lombardis()
 
lombardis1.makeMargherita()
lombardis2.makeMargherita()

 

 

 

Answer:

都包含。 Pizzeria协议声明了makeMargherita()方法并提供了一个默认实现。 该方法在Lombardis实现中被覆盖。 由于在两种情况下在协议中声明该方法,所以在运行时调用正确的实现。
如果协议没有声明makeMargherita()方法,但扩展仍然提供这样的默认实现怎么办?

 

protocol Pizzeria {
  func makePizza(ingredients: [String]) -> Pizza
}
 
extension Pizzeria {
  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "mozzarella"])
  }
}

在这种情况下,只有lombardis2会包含,而lombardis1将没有它,因为它将使用在扩展中定义的方法。

 

 

 

Question #5

以下代码具有编译时错误,找到它!

 

struct Kitten {
}
 
func showKitten(kitten: Kitten?) {
  guard let k = kitten else {
    print("There is no kitten")
  }
 
  print(k)
}

提示:有三处错误。

 

 

 

Answer:

任何guard的else体需要一个退出路径,通过使用return,抛出一个异常或调用一个@noreturn。 最简单的解决方案是添加一个return语句。

 

func showKitten(kitten: Kitten?) {
  guard let k = kitten else {
    print("There is no kitten")
    return
  }
  print(k)
}

这里是一个抛出异常的版本。

 

 

enum KittenError: ErrorType {
  case NoKitten
}
 
struct Kitten {
}
 
func showKitten(kitten: Kitten?) throws {
  guard let k = kitten else {
    print("There is no kitten")
    throw KittenError.NoKitten
  }
  print(k)
}
 
try showKitten(nil)

最后,这里有一个实现调用fatalError(),它是一个@noreturn函数。

 

 

struct Kitten {
}
 
func showKitten(kitten: Kitten?) {
  guard let k = kitten else {
    print("There is no kitten")
    fatalError()
  }
  print(k)
}

 

 

 

 

一些口述问题

 

初学者

Question #1

什么是可选的,可选的什么问题解决?

 

Answer:

可选项用于让任何类型的变量表示缺少值。 在Objective-C中,缺少值仅在引用类型中可用,并且它使用nil特殊值。 值类型,例如int或float,没有这样的能力。
Swift通过可选项将缺少值概念扩展到引用和值类型。 可选变量可以在任何时候保持值或零。

 

 

Question #2

什么时候应该使用结构,什么时候应该使用类?

 

Answer:

关于在结构上使用类是一个好的还是坏的做法,一直存在争论。 函数式编程往往倾向于值类型,而面向对象的编程更喜欢类。
在Swift中,类和结构的特征有很多不同。 您可以将差异总结如下:
类支持继承,结构不支持
类是引用类型,结构是值类型
没有普遍的规则决定什么是最好使用。 一般的建议是使用所需的最小工具来完成你的目标,但一个好的经验规则是使用结构,除非你需要继承或引用语义。
有关详细信息,请查看此详细的帖子。

 

 

Question #3

什么是泛型和他们解决什么问题?

 

Answer:

泛型用于使算法安全地处理类型。 在Swift中,泛型可以在函数和数据类型中使用,例如。 在类,结构或枚举中。
泛型解决了代码重复的问题。 一个常见的情况是当你有一个方法接受一种类型的参数,你必须复制它只是为了适应另一个类型的参数。
例如,在下面的代码中,第二个函数是第一个函数的“clone” - 它只接受字符串而不是整数。

 

func areIntEqual(x: Int, _ y: Int) -> Bool {
  return x == y
}
 
func areStringsEqual(x: String, _ y: String) -> Bool {
  return x == y
}
 
areStringsEqual("ray", "ray") // true
areIntEqual(1, 1) // true

 

在OC中可以用NSObject来解决问题:

 

import Foundation
 
func areTheyEqual(x: NSObject, _ y: NSObject) -> Bool {
  return x == y
}
 
areTheyEqual("ray", "ray") // true
areTheyEqual(1, 1) // true

而在swift你可以利用泛型来很好的解决问题:

 

 

func areTheyEqual<T: Equatable>(x: T, _ y: T) -> Bool {
  return x == y
}
 
areTheyEqual("ray", "ray")
areTheyEqual(1, 1)

 

 

 

Question #4 

有一些情况下,你不能避免使用隐式解包可选。 什么时候? 为什么?

 

Answer:

使用隐式解包可选的最常见原因是:
当不是自然的nil属性不能在实例化时初始化。 一个典型的例子是Interface Builder插件,它在所有者初始化后总是初始化。 在这种特定情况下 - 假设它在Interface Builder中正确配置 - 插座在使用之前保证为非零。
解决强参考周期问题,这是两个实例彼此引用并且需要对其他实例的非空参考。 在这种情况下,引用的一侧可以标记为未知,而另一侧使用隐式解包的可选。

重要提示:不要使用隐式展开的可选,除非你必须。 使用它们不当会增加运行时崩溃的机会。 在某些情况下,崩溃可能是预期的行为,但是有更好的方法来实现相同的结果,例如,通过使用fatalError()。

 

 

Question #5

解包可选的各种方法是什么? 他们如何评价安全性?
提示:至少有七种方式。

 

Answer:

 

  • forced unwrapping ! operator -- unsafe
  • implicitly unwrapped variable declaration -- unsafe in many cases
  • optional binding -- safe
  • optional chaining -- safe
  • nil coalescing operator -- safe
  • new Swift 2.0 guard statement -- safe
  • new Swift 2.0 optional pattern -- safe (suggested by @Kametrixom)

 

 

 

中场

到这里你的表现都非常出色,下面来看看这些问题。

 

Question #1

Swift是一种面向对象的语言还是功能语言?

 

Answer:

Swift是一种支持这两种范例的混合语言。
它实现了OOP的三个基本原则:
- 封装
- 继承
- 多态性
至于Swift是一个功能语言,有不同但等效的方法来定义它。
维基百科上更常见的一种:“...一种将计算视为数学函数的评估,避免改变状态和可变数据的编程范式[...]。
很难说Swift是一个功能齐全的语言,但它有基础。

 

 

Question #2

Swift中包含以下哪些功能?
1.通用类
2.通用结构
3.通用协议

 

Answer:

1和2。 泛型可以在类,结构,枚举,全局函数和方法中使用。
3通过类型部分实现。 它不是一个通用类型本身,它是一个占位符名称。 它通常被称为关联类型,并且当协议被类型采用时被定义。

 

 

Question #3

在OC中,常数一般这样声明:

 

const int number = 0;

这是swift的做法:

 

 

let number = 0

它们之间有什么区别吗? 如果是,你能解释它们有什么不同吗?

 

 

Answer:

const是使用编译时值或在编译时解析的表达式初始化的变量。
通过let创建的不可变是在运行时确定的常量,可以使用静态或动态表达式的结果初始化它。 请注意,其值只能分配一次。

 

 

Question #4

要声明静态属性或函数,请在值类型上使用静态修饰符。 这里有一个结构的例子:

 

struct Sun {
  static func illuminate() {}
}

对于类,可以使用静态或类修饰符。 他们实现同样的目的,但在实际上他们是不同的。 你能解释它们有什么不同吗?

 

 

Answer:

static使一个属性或一个函数静态和不可重写。 通过使用类,可以覆盖属性或函数。
当应用于类时,static将成为类final的别名。
例如,在此代码中,编译器会在尝试覆盖illuminate()时提示:

 

class Star {
  class func spin() {}
  static func illuminate() {}
}
 
class Sun : Star {
  override class func spin() {
    super.spin()
  }
  override static func illuminate() { // error: class method overrides a 'final' class method
    super.illuminate()
  }
}

 

 

 

 

Question #5

可以使用扩展添加存储属性吗? 说明。

 

Answer:

不,这是不可能的。 扩展可用于向现有类型添加新行为,但不能更改类型本身或其接口。 如果添加了存储属性,则需要额外的内存来存储新值。 扩展无法管理此类任务。(表示不服,runtime,大家自己体会)

 

 

高级

朋友,你是一个聪明人,下面准备好难度升级了吗?

 

Question #1 

在Swift 1.2中,你能解释用通用类型声明枚举的问题吗? 例如,具有两个通用类型T和V的Either枚举,T用作左侧案例的关联值类型,V用于右侧案例:

 

enum Either<T, V> {
  case Left(T)
  case Right(V)
}

 

 

 

Answer:

编译会失败,正确做法如下:

 

class Box<T> {
  let value: T
  init(_ value: T) {
    self.value = value
  }
}
 
enum Either<T, V> {
  case Left(Box<T>)
  case Right(Box<V>)
}

 

 

 

 

Question #2

闭包是值类型还是引用类型?

 

Answer:

闭包是引用类型。 如果一个闭包被分配给一个变量,并且该变量被复制到另一个变量中,那么也将复制对同一闭包及其捕获列表的引用。

 

 

Question #3

UInt类型用于存储无符号整数。 它实现以下初始化器,用于从有符号整数转换:

init(_ value: Int)

但是,如果提供负值,以下代码会生成编译时错误异常:

 

 

let myNegative = UInt(-1)

知道一个负数是内部表示的,使用二的补码作为一个正数,你怎么能将一个负数转换为一个UInt,同时保持其内存表示?

 

 

Answer:

使用下面的方式:

 

UInt(bitPattern: Int)

 

 

 

Question #4

你能描述一个你可能在Swift中获得循环引用的情况,以及你将如何解决它?

 

Answer:

循环引用发生在两个实例彼此之间存在强引用时,从而导致内存泄漏,因为两个实例都不会被释放。 原因是实例不能被释放,只要有一个强的引用,但每个实例保持另一个活着,因为它的强引用。
你可以通过强弱循环引用来解决这个问题,用弱引用(weak)或者无主引用(unowned)替换其中一个强引用。

 

 

One More Thing:

看到这里你真的很厉害,相信你也一定收获了很多,希望这些知识可以帮助你在面试中取得好的成绩。如果有问题欢迎留言和加我好友,一起交流交流~

 

前言

Swift语言至今诞生有一年多的时间了,已经成为当前最流行语言之一。虽然它的语法简单好用,但实际上Swift是一门非常复杂的语言。因为它不仅是面向对象的同时又是函数式编程语言。本文主要介绍Swift常见的一些面试问题,你可以用这些问题向面试者提问,也可以用来测试你自己目前所掌握的Swift知识,如果你不清楚问题答案的话也不用太担心,因为每个问题下面都有相应的答案。

一、给一个数组,要求写一个函数,交换数组中的两个元素

二X程序员:

好简单啊,直接写出以下结果

?

1

2

3

4

5

func swap(_ nums: inout [Int], _ p: Int, _ q: Int) {

 let temp = nums[p]

 nums[p] = nums[q]

 nums[q] = temp

}

普通程序员:

首先跟面试官沟通,是什么类型的数组?面试官会说,任意。普通程序员微微一笑,写出以下代码

?

1

2

3

4

5

func swap<T>(_ nums: inout [T], _ p: Int, _ q: Int) {

 let temp = nums[p]

 nums[p] = nums[q]

 nums[q] = temp

}

文艺程序员:

与面试官沟通,是什么类型的数组?有什么其他要求和限制?面试官会说,这是一个Swift面试题。文艺程序员心领神会,于是写出以下答案

?

1

2

3

func swap<T>(_ nums: inout [T], _ p: Int, _ q: Int) {

 (nums[p], nums[q]) = (nums[q], nums[p])

}

同时对以上代码写上相应测试,检测各种边界情况,再确认无误后,才会说,这道题目我完成了。

这道题目看似简单,实际上考察了程序员的审题、交流、以及测试的意识。技术上考察了Swift的泛型和Tuple的性质。

二、下面代码有什么问题

?

1

2

3

4

5

6

7

8

9

public class Node {

 public var value: Int

 public var prev: Node?

 public var post: Node?

 

 public init(_ value: Int) {

 self.value = value

 }

}

答案:应该在 var prev 或者 var post 前面加上 weak。

原因:表面上看,以上代码毫无问题。但是我这样一写,问题就来了:

?

1

2

3

4

let head = Node(0)

let tail = Node(1)

head.post = tail

tail.prev = head

此时,head 和 tail 互相指向,形成循环引用(retain cycle)。

三、实现一个函数,输入是任一整数,输出要返回输入的整数 + 2

这道题很多人上来就这样写:

?

1

2

3

func addTwo(_ num: Int) -> Int {

 return num + 2

}

接下来面试官会说,那假如我要实现 + 4 呢?程序员想了一想,又定义了另一个方法:

?

1

2

3

func addFour(_ num: Int) -> Int {

 return num + 4

}

这时面试官会问,假如我要实现返回 + 6, + 8 的操作呢?能不能只定义一次方法呢?正确的写法是利用 Swift 的柯西特性:

?

1

2

3

4

5

6

7

func add(_ num: Int) -> (Int) -> Int {

 return { val in

 return num + val

 }

}

 

let addTwo = add(2), addFour = add(4), addSix = add(6), addEight = add(8)

四、 精简以下代码

?

1

2

3

4

5

6

7

8

9

10

11

12

func divide(dividend: Double?, by divisor: Double?) -> Double? {

 if dividend == nil {

 return nil

 }

 if divisor == nil {

 return nil

 }

 if divisor == 0 {

 return nil

 }

 return dividend! / divisor!

}

这题考察的是 guard let 语句以及 optional chaining,最佳答案是

?

1

2

3

4

5

6

7

func divide(dividend: Double?, by divisor: Double?) -> Double? {

 guard let dividend = dividend, let divisor = divisor, divisor != 0 else {

 return nil

 }

 

 return dividend / divisor

}

五、以下函数会打印出什么?

?

1

2

3

4

5

6

var car = "Benz"

let closure = { [car] in

 print("I drive \(car)")

}

car = "Tesla"

closure()

因为 clousre 已经申明将 car 复制进去了([car]),此时clousre 里的 car 是个局部变量,不再与外面的 car有关,所以会打印出"I drive Benz"。

此时面试官微微一笑,将题目略作修改如下:

?

1

2

3

4

5

6

var car = "Benz"

let closure = {

 print("I drive \(car)")

}

car = "Tesla"

closure()

此时 closure 没有申明复制拷贝 car,所以clousre 用的还是全局的 car 变量,此时将会打印出 "I drive Tesla"

六、以下代码会打印出什么?

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

protocol Pizzeria {

 func makePizza(_ ingredients: [String])

 func makeMargherita()

}

 

extension Pizzeria {

 func makeMargherita() {

 return makePizza(["tomato", "mozzarella"])

 }

}

 

struct Lombardis: Pizzeria {

 func makePizza(_ ingredients: [String]) {

 print(ingredients)

 }

 func makeMargherita() {

 return makePizza(["tomato", "basil", "mozzarella"])

 }

}

 

let lombardis1: Pizzeria = Lombardis()

let lombardis2: Lombardis = Lombardis()

lombardis1.makeMargherita()

lombardis2.makeMargherita()

答案:打印出如下两行

?

1

2

["tomato", "basil", "mozzarella"]

["tomato", "basil", "mozzarella"]

在Lombardis的代码中,重写了makeMargherita的代码,所以永远调用的是Lombardis 中的 makeMargherita。

再进一步,我们把 protocol Pizzeria 中的 func makeMargherita() 删掉,代码变为

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

protocol Pizzeria {

 func makePizza(_ ingredients: [String])

}

 

extension Pizzeria {

 func makeMargherita() {

 return makePizza(["tomato", "mozzarella"])

 }

}

 

struct Lombardis: Pizzeria {

 func makePizza(_ ingredients: [String]) {

 print(ingredients)

 }

 func makeMargherita() {

 return makePizza(["tomato", "basil", "mozzarella"])

 }

}

 

let lombardis1: Pizzeria = Lombardis()

let lombardis2: Lombardis = Lombardis()

lombardis1.makeMargherita()

lombardis2.makeMargherita()

这时候打印出如下结果:

?

1

2

["tomato", "mozzarella"]

["tomato", "basil", "mozzarella"]

因为lombardis1 是 Pizzeria,而 makeMargherita() 有默认实现,这时候我们调用默认实现。

七、Swift 中定义常量和 Objective-C 中定义常量有什么区别?

一般人会觉得没有差别,因为写出来好像也确实没差别。

OC是这样定义常量的:

?

1

const int number = 0;

Swift 是这样定义常量的:

?

1

let number = 0

首先第一个区别,OC中用 const 来表示常量,而 Swift 中用 let 来判断是不是常量。

上面的区别更进一步说,OC中 const 表明的常量类型和数值是在 compilation time 时确定的;而 Swift 中 let 只是表明常量(只能赋值一次),其类型和值既可以是静态的,也可以是一个动态的计算方法,它们在 runtime 时确定的。

八、Swift 中 struct 和 class 什么区别?举个应用中的实例

struct 是值类型,class 是引用类型。

看过WWDC的人都知道,struct 是苹果推荐的,原因在于它在小数据模型传递和拷贝时比 class 要更安全,在多线程和网络请求时尤其好用。

我们来看一个简单的例子:

?

1

2

3

4

5

6

7

class A {

 var val = 1

}

 

var a = A()

var b = a

b.val = 2

此时 a 的 val 也被改成了 2,因为 a 和 b 都是引用类型,本质上它们指向同一内存。解决这个问题的方法就是使用 struct:

?

1

2

3

4

5

6

7

struct A {

 var val = 1

}

 

var a = A()

var b = a

b.val = 2

此时 A 是struct,值类型,b 和 a 是不同的东西,改变 b 对于 a 没有影响。

九、Swift 到底是面向对象还是函数式的编程语言?

Swift 既是面向对象的,又是函数式的编程语言。

说 Swift 是 Object-oriented,是因为 Swift 支持类的封装、继承、和多态,从这点上来看与 Java 这类纯面向对象的语言几乎毫无差别。

说 Swift 是函数式编程语言,是因为 Swift 支持 map, reduce, filter, flatmap 这类去除中间状态、数学函数式的方法,更加强调运算结果而不是中间过程。

总结

以上就是关于Swift面试题的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Q: switf 2.0增加了一个新的关键字来实现递归枚举。下面的例子是一个枚举类型,它在Node条件下有两个相关联的值类型T和List: enum List{ case Node(T,List)}什么关键字可以实现递归枚举?

答案:indirect关键值可以允许递归枚举,代码:enum List{ indirect case Cons{T,List)}

Q: 描述一种在Swift中出现循环引用的情况,并说明怎么解决

答案:循环引用出现在当两个实例对象相互拥有强引用关系的时候,这会造成内存泄露,原因是这两个对象都不会被释放。只要一个对象被另一个对象强引用,那么该对象就不能被释放,由于强引用的存在,每个对象都会保持对方的存在。

解决方式:用weak或者unowned引用代替其中一个的强引用,来打破循环引用。

Q: UInt类型是用来存储无符号整形的。下面的代码实现了一个有符号整型转换的初始化方法:

init(_ value:Int)

然而,在下面的代码中,当你给一个负值的时候,它会产生一个编译时的错误:

let number = UInt(-1)

我们知道负数的内部结构是使用二进制补码的正数,在保持这个负数内存地址不变的情况下,如何把一个负整数转换成一个无符号的整数?

答案:使用下面的初始化方法:UInt(bitPattern:Int)

Q: 闭包是引用类型吗?

答案:闭包是引用类型。如果一个闭包被分配给一个变量,这个变量复制给另一个变量,那么他们引用的是同一个闭包,他们的捕捉列表也会被复制。

Q: 你能通过extension(扩展)保存一个属性吗?请解释一下原因。

答案:不能。扩展可以给当前的类型添加新的行为,但是不能改变本身的类型或者本身的接口。如果你添加一个新的可存储的属性,你需要额外的内存来存储新的值。扩展并不能实现这样的任务。

Q: 声明一个静态属性或者函数,我们常常使用值类型的static修饰符。下面就是一个结构体的例子:

struct Sun{ static func illuminate(){}}

对类来说,使用static或者class修饰符,都是可以的。它们使用后的效果是一样的,但是本质上是不同的。能解释一下为什么不同吗?

答案:static修饰的属性或者修饰的函数都不可以重写。但是使用class修饰符的,你可以重写属性或者函数。当static在类中应用的时候,static就成为class final的一个别名。

  例如:在下面的代码中,当你尝试重写illuminate()函数时,编译器就会报错;

Q: 在Objective-C中,一个常量可以这样定义:

  const int number = 0

  类似的Swift是这样定义的:

  let number = 0

  两者之间有什么不同吗?如果有,请说明原因。

  答案:const常量是一个在编译时或者编译解析时被初始化的变量。通过let创建的是一个运行时常量,是不可变的。它可以使用stattic或者dynamic关键字来初始化。谨记它的值只能被分配一次。

Q: 下面的功能特性都包含在Swift中吗?

1.泛型类

2.泛型结构体

3.泛型协议

  答案:Swift包含1和2特性。泛型可以在类,结构体,枚举,全局函数或者方法中使用。3是通过typealias部分实现的。typealias不是一个泛型类型,它只是一个占位符的名字。它通常是作为关联类型被引用,只有协议被一个类型引用的时候它才被定义。

Q: Swift是面向对象编程语言还是函数式编程语言?

  Swift是一种混合编程语言,它包含这两种编程模式。它实现了面向对象的三个基本原则:

  •   封装
  •   继承
  •   多态    

  说道Swift作为一个函数式编程语言,我们就不得不说一下什么是函数式编程。有很多不同的方法去定义函数式编程语言,但是他们表达的意义相同。最常见的定义来自维基百科:……它是一种编程规范……它把电脑运算当作数学函数计算,避免状态改变和数据改变。很难说Swift是一个成熟的函数式语言,但是他已经具备了函数式语言的基础。

Q: 对一个optional变量拆包有多少种方法?并在安全方面进行评价。

  答案:强制拆包 !操作符——不安全

     隐式拆包变量生命——大多数情况下不安全

     可选绑定——安全

     自判断链接(optional chaining)——安全

     nil coalescing运算符(空值合并运算符)——安全

     Swift2.0的新特性guard语句——安全

     Swift2.0的新特性optional pattern(可选模式)——安全

Q: 哪些情况下你不得不使用隐式拆包?说明原因。

对optional变量使用隐式拆包最常见的原因如下:

  1.对象属性在初始化的时候不能nil,否则不能被初始化。典型的例子是Interface Builder outlet类型的属性,它总是在它的拥护者初始化之后再初始化。在这种特定的情况下,假设它在Interface Builder中被正确的配置——outlet被使用之前,保证它不为nil。

  2.解决强引用的循环问题——当两个实例对象相互引用,并且对引用的实例对象的值要求不能为nil的时候。在这种情况下,引用的一方可以标记为unowned,另一方使用隐式拆包。

建议:除非必要,不要对option类型使用隐式拆包。使用不当会增加运行时崩溃的可能性。在某些情况下,崩溃可能是有意的行为,但有更好的方法来达到相同的效果,例如,通过使用fatalError()函数。

Q: 什么是泛型?泛型是用来解决什么问题的?

  答案:泛型是用来使类型和算法安全的工作的一种类型。在Swift中,在函数和数据结构中都可以使用泛型,例如类,结构体和枚举。泛型一般是用来解决代码复用的问题。常见的一种情况是,你有一个函数,它带有一个参数,参数类型是A,然而当参数类型改变成B的时候,你不的不复制这个函数。

Q: 在Swift中,什么时候用结构体,什么时候用类?

  答案:在Swift中,类和结构体有许多不同的特性。下面是两者不同的总结:

  类支持继承,结构体不支持。

  类是引用类型,结构体是值类型

  并没有通用的规则决定结构体和类哪一个更好用。一般的建议是使用最小的工具来完成你的目标,但是有一个好的经验是多使用结构体,除非你用了继承和引用语义。注意:在运行时,结构体在性能方面更优于类,原因是结构体的方法调用是静态绑定,而类的方法调用是动态实现的。这就是尽可能得使用结构体代替类的又一个好的原因。

Q: 什么是optional类型,它是用来解决什么问题的?

  答案:optional类型被用来表示任何类型的变量都可以表示缺少值。在Objective-C中,引用类型的变量是可以缺少值的,并且使用nil作为缺少值。基本的数据类型如int或者float没有这种功能。

  Swift用optional扩展了在基本数据类型和引用类型中缺少值的概念。一个optional类型的变量,在任何时候都可以保存一个值或者nil。


 

Q: 下面的代码输出是什么?并说明理由。

  var thing = "cars"    

  let clousure = {[thing] in print("I love (thing)")}

  thing = "airplanes"

  closure()

  答案:输出的是:I love cars。当闭包被声明的时候,捉捕列表就复制一份thing变量,所以被捕捉的值并没有改变,即使你给thing赋予了一个新值。如果你要忽视闭包中捕捉列表的值,那么编译器引用那个值而不是复制。这种情况下,被引用变量的值的变化将会反映到闭包中,正如下面的代码所示:

  var thing = "cars"

  let closure = { print("I love (thing)")}

  thing = "airplanes"

  Prints"I love airplanes"

Q: 思考下面的代码:

  代码:

  var optional1:String? = nil

  var optional2:String? = .None

  nil和.None有什么不同?optional1和optional2有什么不同?

  答案:两者没有什么不同。

  Optional.None(简称.None)是optional变量值初始化的标准方法,而nil只是.None语法的一种修饰。事实上下面语句输出是正确的:

  nil == .None//On Swift1.x this doesnt compile.You need Optional.None记住枚举类型的Optional下的None:

  enum Optional{ case None case Some(T)}

Q: 下面的代码创建了两个类Address和Person,并且创建了两个实例对象分别代表了Ray和Brain.

  class Address{ var fullAddress:String 

  var city:String

  init(fullAddress:String,city:String){ self.city = city}}

  class Person{ var name:String var address:Address

  init(name:String,address:Address){ self.name = name

  self.address = address}} 

  var headquarters = Address(fullAddress:"123 Tutorial Street",city:"Appletown")

  var ray = Person(name:"Ray",address:headquarters)

  var brian = Person(name:"Brian",address:headquarters)

  假设Brain搬家到街对面的建筑物里,那么你会这样更新他的地址:

  brian.address.fullAddress = "148 Tutorial Street"

  这样做将会发生什么?错误出在什么地方呢?

  答案:Ray同样会搬家到新的建筑物里面。Address是一个引用类型类,所以无论你是通过ray或者brain访问headquarters,访问都是同一个实例化对象。headquarters对象的变化也会引起ray和brain的变化。你能想象如果Brain收到Ray的邮件或者相反Ray收到Brain的邮件,将会发生什么?解决方案是创建一个新的Address对象赋值给Brain或者把Address声明成为结构体而不是一个类。

Q: 下面的代码是把数组里面的名字按字母的顺序排序,看上去比较复杂。尽最大的可能简化闭包里的代码。

  let animals = ["fish","cat","chicken","dog"]

  let sortedAnimals = animals.sort{ (one:String,two:String)->Bool in return one < two}

  答案:第一个简化的参数。系统的参数类型推断功能,可以计算出闭包里面参数的类型,所以你不必定义参数的类型推断功能,可以计算出闭包里面参数的类型,所以不必定义参数的类型:

  let sortedAnimals = animals.sort{(one,two)->Bool in return one < two}

  函数返回值也可以被推断出来,所以简化掉,代码变为:

  let sortedAnimas = animals.sort{(one,two)in return one < two}这个$i符号可以代替参数名字,代码进一步简化为:

  let sortedAnimals = animals.sort{return $0 < $1}

  这简化很多了,但是我们我们不能止步于此!

  对于字符串,有一个定义如下的比较函数:

  func Bool

  这个简单的小函数可以使你的代码简洁如下:

  let sortedAnimals = animals.sort(<)注意每一步的编译结果都相同,但是最后一步你的闭包里只有一个字符。

Q: Swift是一门安全语言吗?

  Swift是一门类型安全的语言,Optionals就是代表。Swift能帮助你在类型安全的环境下工作,如果你的代码中需要使用String类型,Swift的安全机制能阻止你错误的将Int值传递过来,这使你在开发阶段就能及时发现并修正问题。

Q: 举例说明Swift里面有哪些是Objective-C中没有的?

  Swift引入了在Objective-C中没有的一些高级数据类型,例如tuples(元组),可以使你创建和传递一组数值。Swift还引入了可选项类型

  (Optionals),用于处理变量值不存在情况。可选项的意思有两种:一是变量是存在的,例如等于X,二是变量值根本不存在。Optionals类似于Objective-C中指向nil的指针,但是适用于所有的数据类型,而非仅仅局限于类,Optionals相比于Objective-C中nil指针更加安全和简明,并且也是Swift诸多最强大功能的核心。

Q: Swift支持面向过程编程吗?

  答案:它采用了Objective-C的命令参数以及动态对象模型,可以无缝对接到现有的Cocoa框架,并且可以兼容Objective-C代码,支持面向过程编程和面相对象编程

Q: Swift的内存管理是怎样的?

  答案:Swift使用自动引用计数(Automatic Reference Counting,ARC)来简化内存管理

Q: Swift比Objective-C有什么优势?

  答案:Swift全面优于Objective-C语言,性能是Objective-C的1.3倍,上手更加容易。

Q: 何时应该使用强引用,弱引用以及无主引用呢?

  强引用:强引用会使得ARC保留实例直到不再需要它们。当移除所有强引用时,引用实例就会被释放。注意默认情况下强引用是隐式的,所以你不必显式地声明它。

  弱引用:你应该在独立生命周期的对象间使用弱引用。当为一个对象设置弱引用时,如果出于内存压力释放了对象,表示你不介意这一点。弱引用的值必须是一个变量,使用var定义,并且必须是使用?运算符的Optional类型。由于弱引用是可选的,所以你决不能以一个已经不存在的无效实例的引用来结束。当引用实例被释放时,ARC将会自动把引用设置为nil。

  无主引用:你应该为有相同生命周期的对象使用无主引用;比如当一个对象指向其自身,以及你希望避免一个retaincycle。无论何时只要引用有一个值就可以使用无主引用,但当你需要告诉ARC不要将它设置为nil时。无主引用的行为类似于Objective-C的unsafe_unretained。你要确保你不会在引用对象被释放后访问引用,这样会导致你的app崩溃。无主引用不能是可选的,不能被设置为nil。无主引用也是隐式解析。

Q: Objective-C中的国际化宏命令在哪儿呢?

  类似Objective-C中的NSLocalizedString,你可以在Swift中使用

  NSLocalizedString(key:tableName:bundle:value:comment:)方法为国际化做准备。tableName,bundle以及value arguments都有默认值,所以如果你正使用NSLocalizedString,你可以编写如下代码:

  dispatch_async(dispatch_get_global_queque(DISPATCH_QUEQUE_PRIORITY_BACKGROUND,0),{

  println("test")});

Q: Swift如何和Grand Central Dispatch一起使用?

  答案:同样的方法,你可以像在Objective-C中那样使用C API。在处理并发性时,你也可以使用苹果高级NSOperationQueue。

Q: 在Swift中有id的等价替代吗?

  答案:有。像上边提到的那样,当Objective-C API返回id类型时,Swift使用AnyObject替换。AnyObjective类型可以代表任何类类型的实例。另外也有Any可代表任何类型的实例(除了函数类型)。

Q: 对于字典(dictionary)也是一样吗?字典也是强类型(strongly typed)的吗?

  答案:是的,不过你依然可以用AnyObject来解决。对于字典来说,它里边所有的值不是同一个类型也讲得通。以下是用字典表示的从服务器端返回的一个JSON响应:

  来看一个服务器JSON响应的例子,用字典来表示:

  var employee:Dictionary<String,AnyObject>=["FirstName":"Larry","LastName":"Rodgers","Salary":65_000.00]

  这个字典有两个String类型的键和一个Double值类型的键。虽然这是可行的,但可能的话你应该创建一级类模型对象来表示数据,而不是依赖字典。

Q: 数组只能包含一个类型的对象吗?如果我想要不同的类型呢?

  答案:在Swift中,强烈建议你使用只包含一种类型的强类型数组,语法像是:

  var goodArray:String[]=["foo","bar"]

  也就是说,从技术上讲,你依然可以创建包含多个类型对象的数组。但最好在做之前问问自己为什么想这么做。按照这种说法,你可以使用AnyObject:创建一个包含不同类型对象的Swift数组:

  var brokenArry:AnyObject[]=["foo",1,12.23,true]


 

 

Q: 你能用Swift来调用自己的Objective-C代码或者第三方库吗?如果能,要怎么做呢?

  答案:可以!当你往Xcode项目里添加第一个.swift文件时,系统会提示你让Xcode创建一个桥接头文件(bridging header file)。你可以在这个头文件中导入你希望可见于Swift代码的Objective-C头文件。然后,所有的类无需导入都可为Swift使用,你可以使用和系统类相同的Swift语法来使用自定义Objective-C文件。

Q: 怎样在Playgrounds中看到那些很酷的值的图形?

  答案:你可以在Playgrounds里绘出值的结果,这对于可视化算法是很方便的。在playground里面输入能够产生值的代码:

for x in 1...10 {

    print("\(x)")

}

  在侧边栏,你会看到类似于“9times”的东西。把鼠标移到这一行上,会出现“+”按钮。点击这个按钮(并确保你打开了Assistant Editor),你将会看到图形。

Q: 有没有Swift可以实现,但Objective-C不能实现的事情?或者反过来说。

  答案:是的。Swift是一门现代语言,引入了很多Objective-C不支持的内容。比如命名空间(namspacing),可选类型(optionals),元组(tuples),泛型(generics),类型推断(type inference)以及其他等等。当然,Objective-C也有一些Swift不具备的特性,比如messaging nil。

Q: playground是什么?

  答案:playground是一个文件,你可以编写代码的同时即可看到运行效果。对于学习Swift或者新的API,原型代码或者算法真的很有用处!

 Q: Swift语言是取代Objective-C,还是对其的补充?

  答案:引用苹果官方的一句话,''Objective-C不会消失,Swfit和Objective-C可同时用于Cocoa和Cocoa Touch开发。''因此,你让然可以继续使用Objective-C。然而,苹果似乎鼓励你使用Swift进行新的开发,而不是希望你重写所有的Objective-C代码。我们猜测苹果在未来的框架和API开发中将会逐渐减少使用Objective-C语言,甚至有一天会弃用Objective-C,所以造作准备吧!

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页