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…

相關文章
相關標籤/搜索