Skip to content

Latest commit

 

History

History
242 lines (188 loc) · 6.57 KB

携程面试题.md

File metadata and controls

242 lines (188 loc) · 6.57 KB

携程面试题

一面 & 二面

1. 请看下面一段代码

static int a = 1;
int main(){
    int m = 2;
    char *n = NULL;
    l = (char *)malloc(100 * sizeof(char));
    return 0;
}

请问访问m,n,l 3种类型变量的效率从高到低依次是() A. lnm B. mnl C. mln D. nlm

2. 请写出一个单例

3. 请写出程序计算结果

AB两地相距1000米,小明从A地点以30米/分钟的速度向B地点走,小白从B地点以20米/分钟的速度向A地点走,两人同时出发,用代码写出他们多少分钟后遇到?

4. 请写出程序输出结果

对数组 ["12-12","12-11", "12-11", "12-11", "12-13", "12-14"] 去重同时进行排序

5. 使用递归方法计算99到1相加的计算结果。是否了解无尾随递归?

6. 请看下面一段代码

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    dispatch_queue_t queue1 = dispatch_get_main_queue();
    dispatch_async(queue1, ^{NSLog(@"222 Hello?");});
    
    NSLog(@"aaaaaaa");
}

A. 死锁 B. 打印“aaaaaaa 222 Hello?” C. 打印“222 Hello? aaaaaaa” D. 打印“”

7. 请说一说UITableView的重用机制

8. 请说一说你对Objective-C中property字段的了解

9. 请说一说你对iOS中内存管理的了解。ARC底层时如何实现的?

10. 你是如何进行网络层的封装的?

11. 你是如何学习iOS开发的?


个人答案(仅供参考)

1. B

2.

Objective-C: 使用dispatch_once_t保证原子性,即init只被调用一次

@implementation MyManager
+ (id)sharedManager {
    static MyManager *staticInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        staticInstance = [[self alloc] init];
    });
    return staticInstance;
}
@end

Swift:Swift有多种方式写单例。

第一种方式: 也是最直接简洁的方式:将实例定义为全局变量。比如下面的代码,声明了一个实例变量sharedManager

let sharedManager = MyManager(string: someString)
class MyManager {
    // Properties
    let string: String
    // Initialization
    init(string: String) {
        self.string = string
    }
}

而如果将上述实例变量在全局命名区(global namespace)第一次调用,由于Swift中全局变量是懒加载(lazy initialize)。所以,在application(_:didFinishLaunchingWithOptions:)中调用的时候之后,shardManager会在AppDelegate类中被初始化,之后程序中所有调用sharedManager实例的地方将都使用该实例。另外,Swift全局变量初始化时默认使用dispatch_once,这保证了全局变量的构造器(initializer)只会被调用一次,保证了shardManager原子性

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // 使用
    print(sharedManager)
    return true
}

第二种方式: Swift 2 开始增加了static关键字,用于限定变量的作用域。如果不使用static(比如let string),那么每一个MyManager实例中均有一个string变量。而使用static之后,shared成为全局变量,成为单例。

class MyManager {
    // 全局变量
    static let shared = MyManager(string: someString)
    
    // Properties
    let string: String
    // Initialization
    private init(string: String) {
        self.string = string
    }
}

第二种方式的使用如下。可以看出采用第二种方式实现单例,代码的可读性增加了,能够直观的分辨出这是一个单例。

// 使用
print(MyManager.shared)

同时,第二种方式还有其他好处。使用了private关键词,保证了只有类自身能够初始化自己。另外,由于不需要在程序已开始就调用单例,所以由于全局变量默认懒加载(lazy initialize),在程序中第一次调用单例时单例才会初始化。

第三种方式: 第三种方式是第二种方式的变种,更加复杂。让单例在闭包(Closure)中初始化,同时加入类方法来获取单例。

class MyManager {
    // 全局变量
    private static let sharedManager: MyManager = {
        let shared = MyManager(string: someString) 
        // 可以做一些其他的配置
        // ...
        return shared
    }()
    // Properties
    let string: String
    // Initialization
    private init(string: String) {
        self.string = string
    }
    // Accessors
    class func shared() -> MyManager {
        return sharedManager
    }
}

可以看出第三种方式虽然更加复杂,但是可以在闭包中作一些额外的配置。同时,调用单例的方式也不一样,需要调用单例中的类方法shared()

print(MyManager.shared())

3. 略

4.

var array = ["12-12","12-11", "12-11", "12-11",  "12-13", "12-14"]
let se = Set(array) // 通过Set去重
array = Array(se)
print(array.sort(<))

5.

普通递归:可能栈溢出crash

func add(n: Int) -> Int {
    if n < 1 { return 0 }
    return n + add(n: n-1)
}
add(n: 99)

尾随递归:把累加结果也传到下一次调用(有些编译器可以对尾随递归进行优化,不过Swift不行,所以仍然可能出现栈溢出crash)

func add2(n:Int, acc: Int = 0) -> Int {
    if n < 1 { return acc }
    return add2(n: n-1, acc: acc + n)
}
add2(n: 99)

使用蹦床(trampolines)机制优化递归:递归执行过程--当前递归入栈、运算、出栈、回调闭包。避免一次性压入过多的栈造成栈溢出(但会比之前的实现慢)

enum Result<A>{  //利用enum作为哨兵值,用来标志递归的结束
    case Done(A)
    case Call(()->Result<A>)
}

func add3(n: Int) -> Int {
    func addTemp(n: Int, acc: Int=1) -> Result<Int> {
        if n<1 { return .Done(acc) }
        return .Call({
            () -> Result<Int> in
            return addTemp(n: n-1, acc: acc+n)
        })
    }
    
    let acc = 0
    var result = addTemp(n: n, acc: acc)
    
    while true {
        switch result {
        case let .Done(accu):
            return accu
        case let .Call(f):
            result = f() // 返回闭包,避免直接递归
        }
    }
}
add3(n: 99)

6. B

7 ~ 11 略


参考资料

SwiftGG - Swift中的尾递归和蹦床

转自 Rickey-iOS-Notes