iOS 第二梯队面试败北感悟 | 掘金技术征文

GitHub Repo:coderZsq.target.swift
Follow: coderZsq · GitHub
Resume: coderzsq.github.io/coderZsq.we…前端

平常扯淡

从去年开始, 我就一直有尝试的面试些大公司, 由于对于一个半路出家(非计算机专业), 靠着MJ视频入门的iOS菜鸡玩家, 通过了3年的摸爬滚打, 终于也渐渐的能够作一些简单的前端, 后端, 移动端的交互, 但想要继续深刻就感受瓶颈愈加的明显, 基础的薄弱致使很难上升, 因此可以进入一家大型成熟互联网公司就成为了我最近的目标, 缘由很简单, 这是最为有效的学习成长的方式.webpack

去年7月, 第一次面试大公司: 饿了么, 收到大公司的召唤很是的兴奋, 以为本身翻身的机会终于要来了, 兴冲冲的跑去面试, 觉得会和通常初级iOS面试的题目相同, 没有作任何的准备, 其实也不知道准备什么, 记得那时候聊的是:git

  1. UI方面: 如何避免卡顿掉帧, 异步渲染.
  2. 性能方面: 性能优化, Vsync, CPU / GPU
  3. 网络方面: 如何进行请求缓存策略.
  4. 安全方面: lild重签名, Mach-O.
  5. 前端方面: 如何避免DOM重绘.
  6. 后端方面: 如何进行负载均衡的处理.

还有一些极端的状况, 因为时间久远已经记不太清了, 反正此次面试给个人感受就是, 靠... 我简直就是个垃圾啊~ 当时记得内推个人架构师建议我扎实一下iOS的基本功, 而后就推荐了基本书: 计算机网络, 操做系统原理, HTTP权威指南, TCP/IP权威指南, 深刻解析Max OS X & iOS 操做系统... 这些书, 除了HTTP权威指南我咬着牙看完了, 其余的对于我来讲简直就是天书, 根本消化不良啊.github

去年12月, 第二次面试大公司: 京东, 因为有了上一次的经验, 我变得很是的淡定, 知道本身确定会被大公司所淘汰, 和优等专业生有着不可逾越天堑, 比较吃惊的是, 进入京东的大楼须要用身份证换取临时门禁... 那时候的面试题就比饿了么的柔和的多了, 虽然当时仍是回答不出.web

  1. Runtime: isa, 消息转发, 弱引用表.
  2. Runloop: mode, timer.
  3. Block: __block, __forwording.
  4. Property: assign, weak, copy.
  5. Category: assoc, load

如今想一想, 这TM才是面试iOS啊, 只惋惜, 那时候并不具有这些知识, 惋惜了了. 以后我就对C++, ASM, Linux, 这三方面进行了学习, 也学习了些MACH-O, 逆向的相关的知识, 以备后面的面试机会.面试

第三次就是这周三面试第二梯队了, 我准备了全部我可以准备的面试题内容, 底层原理, 逆向安全, 多线程与锁, 内存布局, UI性能优化等, 果不其然, 足足1个小时的电话面试, 我轻松经过, 问的就是些我准备好的底层知识, 让我以为机会终于来了, 但是.... 当我现场面试后... 题目所有是上机题...算法

  1. 设计一个网络框架, 如何进行不一样数据解析的设计(header, body), 并可以进行自定义, 重连机制如何处理, 状态码错误转发机制的处理, 如何避免回调地狱, 实现Promise的自实现.
  2. 根据UIControl实现UIButton....
  3. 找到两个排序数组的中位数...
  4. pow(double, double)函数的自实现....

果真,网络, UI, 算法... 好吧, 第一次作上机题, 瞬间就蒙了... 而后就是面试官在旁边不停的笑... 不停的笑... 多是他对我友好的一种方式吧... 最后的面试结论是, 个人知识面仍是比较广的, 作过的东西也是挺多的, 可是在知识深度方面仍是比较欠缺.swift

后来得知, 他们招聘的是技术专家的职级, 以为个人技术水平达不到要求, 不能给与录取, 固然被拒我是当场就知道了, 也以为美团饿了么的两次面试经历都和个人水平相差甚远... 但是, 我只是想进入大公司学习, 就必定要成为专家才行么? 如今初中高级, 资深都不须要了, 直接专家么..., 个人猎头朋友和我说, 3-1的级别就是对标阿里P6/P7, 我选择去死啊.....后端

有了此次的面试经验后, 个人策略也发生了改变, 再也不追求大公司的光环了, 作人开心一点很差么, 非要像机器同样思考? 活的像个算法同样是否是感受缺乏了些重要的东西? 和朋友们吹吹牛逼的日子不快活么, 偏要用功学习? 这里想到了最近听到的两句话, 最好的产品体验就是要让用户不用思考, 认知即痛苦, 无知即极乐, 人真是矛盾, 价值观的一念之差, 差之毫厘失之千里啊....设计模式

面试题1 自实现pow(double, double)

这道题目上机的时候很是的蒙, 由于幂是double, 彻底不知道如何下手, 面试官就下降难度使用整型.

解法1
func _pow_1(_ base: Int, _ exponent: Int) -> Int {

    if exponent < 0 {
        return 0
    }
    if exponent == 0 {
        return 0
    }
    if exponent == 1 {
        return base
    }
    
    var result = base
    for _ in 1..<exponent {
        result *= base
    }
    return result
}
复制代码

而后, 第一次作算法题的我, 只能想到经过最为粗糙的办法解答, 就是一个循环, 我也知道这不是面试官所期许的答案, 但这有什么办法呢...

解法2
func _pow_2(_ base: Double, _ exponent: Int) -> Double {
    
    if exponent < 0 {
        return 0
    }
    if exponent == 0 {
        return 0
    }
    var ans:Double = 1, last_pow = base, exp = exponent
    
    while exp > 0 {
        if (exp & 1) != 0 {
            ans = ans * last_pow
        }
        exp = exp >> 1
        last_pow = last_pow * last_pow
    }
    return ans
}
复制代码

这个是我在网上翻阅资料后的另外一种看似比较好的解答方式.

解法3
func _pow_3(_ base: Double, _ exponent: Int) -> Double {

    var isNegative = false
    var exp = exponent
    if exp < 0 {
        isNegative = true
        exp = -exp
    }
    let result = _pow_2(base, exp)
    return isNegative ? 1 / result : result
}
复制代码

这个仅仅是加了一个负值判断.... 可是幂是double的仍然是毫无头绪, 须要请大佬和大神不吝赐教.

面试题2 findMedianSortedArrays

这是一道LeetCode的原题, 可是我至今尚未刷过算法题库... 也是生平第一次面试的时候遇到算法题. 题目的意思是找到两个排序数组的中位数.

解法1
func findMedianSortedArrays_1(_ array1: [Int], _ array2: [Int]) -> Double {
    
    var array = [Int]()
    array.append(contentsOf: array1)
    array.append(contentsOf: array2)
    
    quickSort(list: &array)
    
    let b = array.count % 2
    let c = array.count
    var result = 0.0;
    if  b == 1  {
        result = Double(array[c / 2])
    } else {
        let n1 = array[c / 2 - 1]
        let n2 = array[c / 2]
        result = Double((n1 + n2)) / 2.0
    }
    
    return result
}
复制代码

第一次作算法题, 只能无视算法复杂度, 可以完成就算是不错了, 要什么自行车...

解法2
func findMedianSortedArrays_2(_ array1: [Int], _ array2: [Int]) -> Double {
    
    let c1 = array1.count, c2 = array2.count
    var a1 = array1, a2 = array2
    if c1 <= 0 && c2 <= 0 {
        return 0.0
    }
    
    func findKth(_ nums1: inout [Int], i: Int, _ nums2: inout [Int], j: Int, k: Int) -> Double {
        if nums1.count - i > nums2.count - j {
            return findKth(&nums2, i: j, &nums1, j: i, k: k)
        }
        if nums1.count == i {
            return Double(nums2[j + k - 1])
        }
        if k == 1 {
            return Double(min(nums1[i], nums2[j]))
        }
        let pa = min(i + k / 2, nums1.count), pb = j + k - pa + i
        if nums1[pa - 1] < nums2[pb - 1] {
            return findKth(&nums1, i: pa, &nums2, j: j, k: k - pa + i)
        } else if nums1[pa - 1] > nums2[pb - 1] {
            return findKth(&nums1, i: i, &nums2, j: pb, k: k - pb + j)
        } else {
            return Double(nums1[pa - 1])
        }
    }
    
    let total = c1 + c2
    if total % 2 == 1 {
        return findKth(&a1, i: 0, &a2, j: 0, k: total / 2 + 1)
    } else {
        return (findKth(&a1, i: 0, &a2, j: 0, k: total / 2) + findKth(&a1, i: 0, &a2, j: 0, k: total / 2 + 1)) / 2.0
    }
}
复制代码

这个是我在网上查资料的时候的答案... 还没理清是个什么思路, 反正面试官提示分而治之, 掐头去尾... 也不知道是否是最优算法.

解法3
func findMedianSortedArrays_3(_ array1: [Int], _ array2: [Int]) -> Double {
    
    let total = array1.count + array2.count
    let index = total / 2
    let count = array1.count < array2.count ? array2.count : array1.count
    var array = [Int]()
    
    var i = 0, j = 0;
    for _ in 0...count {
        if array.count >= index + 1 {
            break
        }
        if array1[i] < array2[j] {
            array.append(array1[i])
            i += 1
        } else {
            array.append(array2[j])
            j += 1
        }
    }
    return total % 2 == 1 ? Double(array[index]) : Double(array[index] + array[index - 1]) * 0.5
}
复制代码

这个是请教霜神(@halfrost-一缕殇流化隐半边冰霜)后给的思路, 的确很好实现. 但霜神谦虚的说不是最优解....

奇数测试
var array1 = randomList(1000001)
var array2 = randomList(1000000)
quickSort(list: &array1)
quickSort(list: &array2)
print(findMedianSortedArrays_1(array1, array2))
print(findMedianSortedArrays_2(array1, array2))
print(findMedianSortedArrays_3(array1, array2))
复制代码
--- scope of: findMedianSortedArrays ---
500045.0
500045.0
500045.0
复制代码
偶数测试
var array1 = randomList(1000001)
var array2 = randomList(1000000)
quickSort(list: &array1)
quickSort(list: &array2)
print(findMedianSortedArrays_1(array1, array2))
print(findMedianSortedArrays_2(array1, array2))
print(findMedianSortedArrays_3(array1, array2))
复制代码
--- scope of: findMedianSortedArrays ---
499665.5
499665.5
499665.5
复制代码
耗时比较
--- scope of: findMedianSortedArrays_1 ---
timing: 2.50845623016357
--- scope of: findMedianSortedArrays_2 ---
timing: 1.28746032714844e-05
--- scope of: findMedianSortedArrays_3 ---
timing: 0.0358490943908691
复制代码

能够看出网上查资料的答案是三种解法里性能最高的算法, 霜神的思路和网上的答案差了三个数量级, 而我写的差了五个数量级.... 果真我写的果真是最为垃圾的算法....

解法4
@discardableResult func findMedianSortedArrays_4(_ array1: [Int], _ array2: [Int]) -> Double {
    
    if array1.count == 0 {
        if array2.count % 2 == 1 {
            return Double(array2[array2.count / 2])
        } else {
            return Double(array2[array2.count / 2] + array2[array2.count / 2 - 1]) * 0.5
        }
    } else if array2.count == 0 {
        if array1.count % 2 == 1 {
            return Double(array1[array1.count / 2])
        } else {
            return Double(array1[array1.count / 2] + array1[array1.count / 2 - 1]) * 0.5
        }
    }
    
    let total = array1.count + array2.count
    let count = array1.count < array2.count ? array1.count : array2.count
    let odd = total % 2 == 1
    
    var i = 0, j = 0, f = 1, m1 = 0.0, m2 = 0.0, result = 0.0;
    for _ in 0...count {
        if odd { array1[i] < array2[j] ? (i += 1) : (j += 1) }
        if f >= total / 2 {
            if odd {
                result = array1[i] < array2[j] ? Double(array1[i]) : Double(array2[j])
            } else {
                if array1[i] < array2[j] {
                    m1 = Double(array1[i])
                    if (i + 1) < array1.count && array1[i + 1] < array2[j] {
                        m2 = Double(array1[i + 1])
                    } else {
                        m2 = Double(array2[j])
                    }
                } else {
                    m1 = Double(array2[j])
                    if (j + 1) < array2.count && array2[j + 1] < array1[i] {
                        m2 = Double(array2[j + 1])
                    } else {
                        m2 = Double(array1[i])
                    }
                }
                result = (m1 + m2) * 0.5
            }
            break
        }
        if !odd { array1[i] < array2[j] ? (i += 1) : (j += 1) }
        f += 1
    }
    return result
}
复制代码
--- scope of: findMedianSortedArrays_3 ---
timing: 0.0358932018280029
--- scope of: findMedianSortedArrays_4 ---
timing: 0.0241639614105225
复制代码

沿着霜神的思路和面试官给的提示, 给出了上面的算法, 可是解法3的数量级是相同的

面试题3 UIContorl -> UIButton

protocol ButtonInterface {
    func setTitle(_ title: String);
    func setTitleColor(_ titleColor: UIColor);
    func setTitleEdgeInsets(_ edgeInsets: UIEdgeInsets);
    func setImage(_ image: UIImage);
    func setBackgroundImage(_ image: UIImage);
    func setImageEdgeInsets(_ edgeInsets: UIEdgeInsets);
}

class Button: UIControl, ButtonInterface { 

}
复制代码

以上就是面试时候的原题, 一开始根本不知道是要让我作些什么, 说是只要让我把上面的方法所有实现就行了, 就像实现一个本身的UIButton... 一开始, 我觉得是要我用CALayer来实现, 吓的我瑟瑟发抖... 还好不是... 而后, 我就按照本身平时自定义控件的写法, 写了一个UIImageView, 一个UILabel, 而后布局赋值... 就看到面试官对着我笑着说, 你觉得这道题这么简单么... 这么简单么...

而后说, 你知道UIButton setTitle的时候才会建立UILabel, setImage的时候才会建立UIImageView, 你为何吧frame给写死... 不知道UIViewsizeToFit么, 你怎么不实现sizeThatFits, 你是彻底不会用吧... 你知道UIButtonAutoLayout布局的时候只要设置origin坐标, 宽高就能够自适应了, 你自定义的时候怎么不实现呢? setBackgroundImagesetImageEdgeInsets你就不要作了吧, 反正你也不会...

我想说的是,谁没事放着UIButton不用, 用UIContorl这种东西... 就为了一个target-action的设计模式么... 我每次在想思路的时候一直打断我, 可能这是面试官的一种策略吧... 算了不吐槽了, 仍是尽力实现吧.

import UIKit

protocol ButtonInterface {
    func setTitle(_ title: String);
    func setTitleColor(_ titleColor: UIColor);
    func setTitleEdgeInsets(_ edgeInsets: UIEdgeInsets);
    func setImage(_ image: UIImage);
    func setBackgroundImage(_ image: UIImage);
    func setImageEdgeInsets(_ edgeInsets: UIEdgeInsets);
}

class Button: UIControl, ButtonInterface {

    lazy var titleLabel: UILabel = UILabel()
    lazy var imageView: UIImageView = UIImageView()
    lazy var backgroundImageView: UIImageView = UIImageView()
    
    var titleLabelIsCreated = false
    var imageViewIsCreated = false
    var backgroundImageViewCreated = false
    
    internal func setTitle(_ text: String) {
        if !titleLabelIsCreated {
            addSubview(titleLabel)
            titleLabelIsCreated = true
        }
        titleLabel.text = text
    }

    internal func setTitleColor(_ textColor: UIColor) {
        if !titleLabelIsCreated {
            return
        }
        titleLabel.textColor = textColor
    }
    
    internal func setTitleEdgeInsets(_ edgeInsets: UIEdgeInsets) {
        if !titleLabelIsCreated {
            return
        }
    }
    
    internal func setImage(_ image: UIImage) {
        if !imageViewIsCreated {
            addSubview(imageView)
            imageViewIsCreated = true
        }
        imageView.image = image
    }
    
    internal func setBackgroundImage(_ image: UIImage) {
        if !backgroundImageViewCreated {
            addSubview(backgroundImageView)
            insertSubview(backgroundImageView, at: 0)
            backgroundImageViewCreated = true
        }
        backgroundImageView.image = image
    }
    
    internal func setImageEdgeInsets(_ edgeInsets: UIEdgeInsets) {
        if !imageViewIsCreated {
            return
        }
    }
    
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        if titleLabelIsCreated && !imageViewIsCreated && !backgroundImageViewCreated {
            let text: NSString? = titleLabel.text as NSString?
            let titleLabelW: CGFloat = text?.boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: bounds.height), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.width ?? 0.0
            let titleLabelH: CGFloat = text?.boundingRect(with: CGSize(width: titleLabelW, height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.height ?? 0.0
            return CGSize(width: titleLabelW, height: titleLabelH + 10)
        } else if !titleLabelIsCreated && imageViewIsCreated {
            return imageView.image?.size ?? CGSize.zero
        } else if titleLabelIsCreated && imageViewIsCreated {
            let text: NSString? = titleLabel.text as NSString?
            let titleLabelW: CGFloat = text?.boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: bounds.height), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.width ?? 0.0
            let titleLabelH: CGFloat = text?.boundingRect(with: CGSize(width: titleLabelW, height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.height ?? 0.0
            let imageViewW: CGFloat = imageView.image?.size.width ?? 0.0
            let imageViewH: CGFloat = imageView.image?.size.height ?? 0.0
            return CGSize(width: titleLabelW + imageViewW, height: imageViewH > titleLabelH ? imageViewH : titleLabelH)
        } else {
            return backgroundImageView.image?.size ?? CGSize.zero
        }
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()

        if titleLabelIsCreated && !imageViewIsCreated {
            titleLabel.frame = bounds
            titleLabel.textAlignment = .center
        } else if !titleLabelIsCreated && imageViewIsCreated {
            let y: CGFloat = 0;
            let width: CGFloat = imageView.image?.size.width ?? 0;
            let x: CGFloat = (bounds.width - width) * 0.5;
            let height: CGFloat = bounds.height;
            imageView.frame = CGRect(x: x, y: y, width: width, height: height)
        } else if titleLabelIsCreated && imageViewIsCreated {
            let imageViewY: CGFloat = 0;
            let imageViewW: CGFloat = imageView.image?.size.width ?? 0;
            let imageViewH: CGFloat = bounds.height;
            let text: NSString? = titleLabel.text as NSString?
            let titleLabelW: CGFloat = text?.boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: bounds.height), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.width ?? 0.0
            let titleLabelH: CGFloat = text?.boundingRect(with: CGSize(width: titleLabelW, height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.height ?? 0.0
            let imageViewX: CGFloat = (bounds.width - imageViewW - titleLabelW) * 0.5;
            let titleLabelX: CGFloat = imageViewX + imageViewW
            let titleLabelY = (bounds.height - titleLabelH) * 0.5
            titleLabel.frame = CGRect(x: titleLabelX, y: titleLabelY, width: titleLabelW, height: titleLabelH)
            imageView.frame = CGRect(x: imageViewX, y: imageViewY, width: imageViewW, height: imageViewH)
        }
        
        if backgroundImageViewCreated {
            backgroundImageView.frame = bounds
        }
    }
}
复制代码

虽然实现了部分的功能, 可是AutoLayoutEdgeInsets的功能仍是没有思路, 还请各位大佬解惑.

测试对比

咱们用本身自实现的ButtonUIButton进行对比.

class ViewController: UIViewController {

    override func loadView() {
        super.loadView();
        
        let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
        uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
        uibutton.setImage(UIImage(named: "avatar"), for: .normal)
        uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
        uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
        uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
        button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        button.setImage(UIImage(named: "avatar") ?? UIImage());
        button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
        button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
        button.sizeToFit()
    }
}
复制代码
zero impl
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
// uibutton.setTitle("github.com/coderZsq", for: .normal)
// uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
// uibutton.setImage(UIImage(named: "avatar"), for: .normal)
// uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
// uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
// button.setTitle("github.com/coderZsq")
// button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
// button.setImage(UIImage(named: "avatar") ?? UIImage());
// button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
// button.sizeToFit()

复制代码

setTitle && setTitleColor
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
// uibutton.setImage(UIImage(named: "avatar"), for: .normal)
// uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
// uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
// button.setImage(UIImage(named: "avatar") ?? UIImage());
// button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
// button.sizeToFit()
    }
复制代码

setTitle && setTitleColor && sizeToFit
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
// uibutton.setImage(UIImage(named: "avatar"), for: .normal)
// uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
        uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
// button.setImage(UIImage(named: "avatar") ?? UIImage());
// button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
        button.sizeToFit()
复制代码

setImage
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
// uibutton.setTitle("github.com/coderZsq", for: .normal)
// uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
        uibutton.setImage(UIImage(named: "avatar"), for: .normal)
// uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
// uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
// button.setTitle("github.com/coderZsq")
// button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        button.setImage(UIImage(named: "avatar") ?? UIImage());
// button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
// button.sizeToFit()
复制代码

setImage && sizeToFit
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
// uibutton.setTitle("github.com/coderZsq", for: .normal)
// uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
        uibutton.setImage(UIImage(named: "avatar"), for: .normal)
// uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
        uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
// button.setTitle("github.com/coderZsq")
// button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        button.setImage(UIImage(named: "avatar") ?? UIImage());
// button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
        button.sizeToFit()
复制代码

setBackgroundImage
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
// uibutton.setTitle("github.com/coderZsq", for: .normal)
// uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
// uibutton.setImage(UIImage(named: "avatar"), for: .normal)
        uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
// uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
// button.setTitle("github.com/coderZsq")
// button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
// button.setImage(UIImage(named: "avatar") ?? UIImage());
        button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
// button.sizeToFit()

复制代码

这里, 咱们看到和系统的实现不同, 由于我直接在渲染(display)设置了bitmap, 而不是像系统添加了一个新的view. 这样的性能消耗会少些... (以修改 为了下面的问题)

setBackgroundImage && sizeToFit
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
// uibutton.setTitle("github.com/coderZsq", for: .normal)
// uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
// uibutton.setImage(UIImage(named: "avatar"), for: .normal)
        uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
        uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
// button.setTitle("github.com/coderZsq")
// button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
// button.setImage(UIImage(named: "avatar") ?? UIImage());
        button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
        button.sizeToFit()

复制代码

setTitle && setTitleColor && setImage
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
        uibutton.setImage(UIImage(named: "avatar"), for: .normal)
// uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
// uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        button.setImage(UIImage(named: "avatar") ?? UIImage());
// button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
// button.sizeToFit()

复制代码

setTitle && setTitleColor && setImage && sizeToFit
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
        uibutton.setImage(UIImage(named: "avatar"), for: .normal)
// uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
        uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        button.setImage(UIImage(named: "avatar") ?? UIImage());
// button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
        button.sizeToFit()
复制代码

setTitle && setTitleColor && setBackgroundImage
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
// uibutton.setImage(UIImage(named: "avatar"), for: .normal)
        uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
// uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
// button.setImage(UIImage(named: "avatar") ?? UIImage());
        button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
// button.sizeToFit()
复制代码

setTitle && setTitleColor && setBackgroundImage && sizeToFit
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
// uibutton.setImage(UIImage(named: "avatar"), for: .normal)
        uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
        uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
// button.setImage(UIImage(named: "avatar") ?? UIImage());
        button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
        button.sizeToFit()
复制代码

setTitle && setTitleColor && setImage && setBackgroundImage
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
        uibutton.setImage(UIImage(named: "avatar"), for: .normal)
        uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
// uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        button.setImage(UIImage(named: "avatar") ?? UIImage());
        button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
// button.sizeToFit()
复制代码

setTitle && setTitleColor && setImage && setBackgroundImage && sizeToFit
let uibutton = UIButton()
        uibutton.layer.borderWidth = 1
        uibutton.layer.borderColor = UIColor.black.cgColor
        
        uibutton.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 40)
        uibutton.setTitle("github.com/coderZsq", for: .normal)
        uibutton.setTitleColor(.red, for: .normal)
// uibutton.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
        uibutton.setImage(UIImage(named: "avatar"), for: .normal)
        uibutton.setBackgroundImage(UIImage(named: "avatar"), for: .normal)
// uibutton.imageEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
        view.addSubview(uibutton)
        uibutton.sizeToFit()
        
        let button = Button()
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.black.cgColor
        
        button.frame = CGRect(x: 0, y: 350, width: view.bounds.width, height: 40)
        button.setTitle("github.com/coderZsq")
        button.setTitleColor(.red)
// button.setTitleEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        button.setImage(UIImage(named: "avatar") ?? UIImage());
        button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
// button.setImageEdgeInsets(UIEdgeInsetsMake(0, 0, 0, 0))
        view.addSubview(button)
        button.sizeToFit()
复制代码

这道题尚未实现的就是AutoLayoutEdgeInsets, 还有就是在sizeToFit算的最佳尺寸和系统的最佳尺寸有细微出入, 还有就是UIImageViewUILabel的前后加载的问题.

update
lazy var titleLabel: UILabel =  {
        let titleLabel = UILabel()
        titleLabel.font = UIFont.systemFont(ofSize: 18)
        titleLabel.textAlignment = .center
        return titleLabel
    }()
复制代码

对于在sizeToFit算的最佳尺寸和系统的最佳尺寸有细微出入 这个问题是UILabel的默认系统字体大小是17, 而UIButton中的titleLabel的字体大小是18.

internal func setImage(_ image: UIImage) {
        if !imageViewIsCreated {
            addSubview(imageView)
            if titleLabelIsCreated {
                insertSubview(imageView, belowSubview: titleLabel)
            }
            imageViewIsCreated = true
        }
        imageView.image = image
    }
复制代码

对于UIImageView和UILabel的前后加载的问题, 须要在setImage时判断titleLabel是否存在便可

现已达到彻底相同, 接下来就是要解决AutoLayoutEdgeInsets的问题.

update2

经过 nib 进行建立.. 谢谢@梦痕_Lee提供的思路.

实现方法:

1.判断是不是从nib中建立

var createdFromNib = false
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        createdFromNib = true
    }
复制代码

2.重写系统实现函数intrinsicContentSize并返回最佳尺寸

override var intrinsicContentSize: CGSize {
        return bestSize()
    }
    
    func bestSize() -> CGSize {
        if titleLabelIsCreated && !imageViewIsCreated && !backgroundImageViewCreated {
            let text: NSString? = titleLabel.text as NSString?
            let titleLabelW: CGFloat = text?.boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: bounds.height), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.width ?? 0.0
            let titleLabelH: CGFloat = text?.boundingRect(with: CGSize(width: titleLabelW, height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.height ?? 0.0
            return CGSize(width: titleLabelW, height: titleLabelH + 10)
        } else if !titleLabelIsCreated && imageViewIsCreated {
            return imageView.image?.size ?? CGSize.zero
        } else if titleLabelIsCreated && imageViewIsCreated {
            let text: NSString? = titleLabel.text as NSString?
            let titleLabelW: CGFloat = text?.boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: bounds.height), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.width ?? 0.0
            let titleLabelH: CGFloat = text?.boundingRect(with: CGSize(width: titleLabelW, height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font : titleLabel.font], context: nil).size.height ?? 0.0
            let imageViewW: CGFloat = imageView.image?.size.width ?? 0.0
            let imageViewH: CGFloat = imageView.image?.size.height ?? 0.0
            return CGSize(width: titleLabelW + imageViewW, height: imageViewH > titleLabelH ? imageViewH : titleLabelH)
        } else {
            return backgroundImageView.image?.size ?? CGSize.zero
        }
    }
复制代码

3.布局时判断便可

override func layoutSubviews() {
        super.layoutSubviews()

        if createdFromNib {
            frame.size = intrinsicContentSize
        }
        ...
   }
复制代码

4.测试一下, 不加sizetofit

nib_button.layer.borderWidth = 1
        nib_button.layer.borderColor = UIColor.black.cgColor
        
        nib_button.setTitle("github.com/coderZsq")
        nib_button.setTitleColor(.red)
// nib_button.setTitleEdgeInsets(.zero)
        nib_button.setImage(UIImage(named: "avatar") ?? UIImage())
// nib_button.setBackgroundImage(UIImage(named: "avatar") ?? UIImage())
        nib_button.setImageEdgeInsets(.zero)
复制代码

同样的完美, 攻克了AutoLayout, 接下来就是EdgeInsets啦~~~

update3

一直理解错了, intrinsicContentSize这个方法是在添加约束后生效, 不论是nib仍是手动约束, 都会生效.

因此update2中只需实现第二步便可, 并不须要其余的判断.

面试题4 网络架构实现

这道题真是戳中个人软肋, 网络与多线程是我最为薄弱的地方, 如今对线程的理解应该已经不错了, 可是网络仍是有所欠缺的, 因此, 我会在从此学习Linux和精读AFNetWorking && SDWebImage后, 本身写一个网络架构来进行自我提高.

面试总结

对于两道算法题, 我没有什么太多想讲的, 技不如人吧, 但是算法题这种东西在iOS上用处其实不是很大, 我就不信那些没有刷过算法题的同窗, 面对一道从没有见过的算法题可以一会儿从容的写出最优解的. 还有就是UI的那道题目, 坑吧, 谁会去放着好好的现成的不用, 去恶心本身, 还必定要同样... 我都问过面试官好几遍, 到底想要作什么功能... 回答只有, 我不在意你怎么写, 只要和系统的同样就行了.... 产品附体了么....

好吧... 技不如人, 被挂了也是理所固然... 不会找什么借口... 听美团的朋友说, 如今只招技术专家, 其余低职级的名额紧缩都不招了... 诶... 随缘吧...

最后 本文中全部的源码均可以在github上找到:

GitHub Repo:coderZsq.target.swift
Follow: coderZsq · GitHub
Resume: coderzsq.github.io/coderZsq.we…

掘金技术征文连接👉 juejin.im/post/5aaf2a…

相关文章
相关标签/搜索