一面 & 二面
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
AB两地相距1000米,小明从A地点以30米/分钟的速度向B地点走,小白从B地点以20米/分钟的速度向A地点走,两人同时出发,用代码写出他们多少分钟后遇到?
对数组 ["12-12","12-11", "12-11", "12-11", "12-13", "12-14"] 去重同时进行排序
- (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. 打印“”
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())
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(<))
普通递归:可能栈溢出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)