iOS面试题(一)

1,476 阅读4分钟

第一次电话面试

按照顺序打印 1 到 100 不少于一个线程

        //全局变量
        var   lock = os_unfair_lock()
        
        DispatchQueue.global().async {
            self.action()
        }
        DispatchQueue.global().async {
            self.action()
        }
        //具体打印
    func action(){

        while true {
           
            os_unfair_lock_lock(&lock)

            if num >= 100{
                os_unfair_lock_unlock(&lock)
                return
            }
            num += 1
            
            print("\(num)----\(Thread.current)")
            
            os_unfair_lock_unlock(&lock)

        }

    }
        

全局并发队列打印1 到 10000 最终结果

  • 会少于 10000 因为会同时拿到 num 值 进行加一

MVVM view 和 viewmodel 和 model 的关系

  • viewModel持有 model view 持有 viewModel 使 view 和 model 实现双向绑定

不用 RAC 怎么实现 MVVM

  • 利用 KVO 实现双向绑定

UIView.animate.during 监听 alpha 0 到 1的变化

       let normalView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        normalView.backgroundColor = UIColor.red
        normalView.alpha = 0
        view.addSubview(normalView)
        
        let timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { (time) in
            let layer = normalView.layer.presentation()
            
            print(layer?.opacity)
        }
        RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
        UIView.animate(withDuration: 2) {
            normalView.alpha = 1
        }

setNeedlayout layouSubViews等

  • layouSubViews的调用时机
    • init初始化不会触发layoutSubviews。
    • addSubview会触发layoutSubviews(前面 init 如果设置 frame 的话会调用两次,如果没有 设置frame 就调用一次)
    • 设置view的Frame会触发layoutSubviews,(当然前提是frame的值设置前后发生了变化。单单设置 x 值的变化是不会调的,但设置 y 值是会变的,奇怪)
    • 滚动一个UIScrollView会触发layoutSubviews。
    • 旋转屏幕会触发父UIView上的layoutSubviews事件。(这个我们开发中会经常遇到,比如屏幕旋转时,为了界面美观我们需要修改子view的frame,那就会在layoutSubview中做相应的操作)
    • 改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
    • 直接调用setLayoutSubviews。(Apple是不建议这么做的)
  • drawRect调用时机
    • 如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。drawRect 掉用是在Controller->loadView, Controller->viewDidLoad 两方法之后掉用的.所以不用担心在 控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View draw的时候需要用到某些变量值).

    • 该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。

    • 通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。

    • 直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。

组件化间传值 组件化方案优劣

  • 传值总的来说 前向传值就是直接传,反向传值用 block 等
    • MGJRouter
      //MGJRouter
      //前向
      [MGJRouter openURL:@"MGJ://Test2/PushMainVC" withUserInfo:@{@"navigationVC":self.navigationController,@"text":@"前向传值"} completion:nil];
      //反向
      [MGJRouter openURL:@"MGJ://Test3/PushMainVC"      withUserInfo:@{@"navigationVC":self.navigationController,
                      @"block":^(NSString *text){
                           NSLog(@"%@",text);
                     }} completion:nil];
      
      
    • CTMediator
       @objc public func A_showSwift(param:[AnyHashable:Any], callback:@escaping (String) -> Void) -> UIViewController? {
         var params = param//前向
         params["callback"] = callback//反向
         params[kCTMediatorParamsKeySwiftTargetModuleName] = "DYLCWithDrawKit"
         
         if let viewController = self.performTarget("DYLCWithDrawKit", action: "Extension_ViewController", params: params, shouldCacheTarget: false) as? UIViewController {
             return viewController
         }
         return nil
      }
      
      
          @objc func Action_Extension_ViewController(_ params:NSDictionary) -> UIViewController {
         
         
         let  num =   params["keggg"]//前向
         let aViewController = SHWithDrawHomeViewController()
         aViewController.num = num as! Int//前向
         aViewController.callBack = { (str) in
             if let callback = params["callback"] as? (String) -> Void {
                 callback(str)//反向
             }
         }
         return aViewController
      }
      
      
  • 组件化的优劣

下载100张大图进行组合

  • 队列组
  • NSOperationQueue线程依赖

单例的销毁

  • 代码中有一个onceToken变量,在单例生成之前onceToken = 0,在单例生成之后onceToken = -1了,之后一直保持-1这个值,销毁的时候把单例类置为 nil 同时把onceToken置为 0

工厂模式的实际应用

内存泄漏怎么检查

  • MFinderLeaks,instrement 动态检查,静态分析

AutoLayout怎么实现界面布局的

  • AutoLayout用到了布局算法Cassowary算法和一整套布局引擎Layout Engine

  • 每个视图在得到它的布局之前,Layout Engine会将视图、约束、优先级、固定大小通过计算转换成最终的位置和大小。

  • 具体工作流程:

    • 当约束变化,添加、删除视图、设置constant和priority会触发约束变化
    • Layout Engine碰到约束变化时会重新计算布局
    • 获取到新约束后,调用superview.setNeedLayout()
    • 然后会进入Deferred Layout Pass,主要是做一些容错处理。比如说有约束缺失或者没有确定,会在这里做容错处理
    • 接下来,Layout Engine 会从上到下调用 layoutSubviews() ,通过 Cassowary 算法计算各个子视图的位置,算出来后将子视图的 frame 从 Layout Engine 里拷贝出来
    • 最后,当runloop到来的时候,去重新绘制操作(这一步跟手动布局一样)