淺談我對iOS編程的一點理解

0x01:編程是什麼

· 認識我們要控制的角色

談到編程,說到底就是控制計算機按照一定的邏輯將某些數值去做運算的過程。

  • 負責運算的角色叫CPU
  • 數據的記錄就需要佔用存儲空間,這些存儲空間又叫緩存
  • 根據緩存的讀寫速度及重要程度,又劃分了一級緩存、二級緩存、三級緩存···
    1 一級緩存:CPU讀寫的數據要有地方存,這些“地方”就是寄存器
    2 二級緩存:受CPU規模所限,就那麼幾個寄存器,只能挑一些指令必要的數據存儲,剩下的程序必要數據可以放到CPU外面——內存
    3 三級緩存:由於還有一些數據需要斷電後被記錄,且對程序運行來講不是那麼重要,所以就有了更廉價的存儲器——硬盤

至此,我們編程所需要關心的主要角色都到齊了。
一般情況下,需要我們要控制的東西有:CPU、寄存器、內存。
所用的原力就是:高低電平。可以用二進制表示成“01001”這種指令。

· 簡化我們的魔法書

我們是人類啊,記一句“急急如律令”還行,“000111010101”這些東西怎麼記?於是乎,聰明的人類對魔法書進行了第一次修改:人們在魔法書上列了一張2 x n的表格,左邊是0101,右邊就是MOV、ADD等指令。魔法書從此改名了,叫彙編

· 豐富多彩的魔法書

隨着社會的發展,編程需求越來越多,代碼體量越來越大,彙編代碼越來越不好調試、管理、維護,於是聰明的人們有開始想辦法了。
不就是要寫指令、管理內存嘛!指令再用更通俗的詞、句標識,內存的管理往數學模型上靠,於是就有了數據結構高級指令,有機的結合在一起,新的魔法書誕生了:語言。出於不同的目的和需要,魔法書從此豐富多彩:C、Java、C#、Python、Dart。。。

· 機器怎麼識別新魔法

語言是給我們看的,計算機看不懂,它需要一個翻譯器。什麼時候翻譯?

  • 從本本上讀一句,給機器解釋一句:這就是腳本語言。
  • 把本本1上的所有句子翻譯完,寫成本本2,交給機器自己看:這就是編譯語言。

0x02:淺談iOS編程

· 我們寫的代碼是給誰看的

以前爲了驗證autorelease相關的理論,用clang翻譯了一下OC代碼,看到基本的都是runtime的東西。那一刻我突然明白了一個道理:你以爲編譯器(具體指編譯器後端)要翻譯的代碼就是你寫的代碼嗎?不是的!我們寫的代碼不是給機器看的,是給clang前端看的。 等到編譯器準備把上層代碼翻譯成彙編的時候,這些代碼早就不是我們寫的模樣了,全被clang翻譯成了c、c++代碼。
所以面試的時候人家會問你:“KVO原理是什麼?Block原理是什麼?” 而不是問:“KVO源碼是什麼?Block源碼是什麼?” 因爲上層KVO、block代碼,是被clang按照固定格式重寫成了一大坨c++代碼,這個固定格式的寫法其實就是原理。並沒有KVO、Block源碼一說。

· 我們寫的代碼是什麼意思

之前因爲逆向,學習了一段時間彙編,至此我才真正明白了內存、指針、對象以及我寫的代碼是什麼意思。
舉個簡單的例子:

/// Student.h
@interface Student : NSObject
@property (nonatomic, assign) long age;
@end
/* 代碼案例 */
Studen *s = [[Student alloc] init];
/// 這裏的s是什麼?是對象?還是指針?
/// 對象是什麼?對象存在哪裏?你有對象了麼?(咳咳,最後這個是開個玩笑)

假如你已經思考過了,那現在來看結論吧。請務必先思考一下。

[[Student alloc] init]//在堆內存上開闢一塊空間,不考慮內存對齊,大小是Student對應的結構體大小。
Student *s = Student對象; //創建一個Student *類型的指針,指針s的值 是Student對象的首地址。

乾貨:

  • 說s是對象,也沒問題.因爲我們說s的時候,一般都是指s的值。
    它的值就是Student實例對象的地址;也就表示了該對象。
  • 如果s是局部變量,那就在棧區上,如果是全局變量,那就在全局區(靜態區);
  • 堆、棧、代碼段、數據段、靜態區···這些概念都是我們爲了方便管理內存,虛擬出來的一些區域。甚至連我們平常所說的內存都是虛擬的。
  • 其實這些概念就是約定的協議。比如在iOS arm64架構中,0x0~0xffffffff這段虛擬內存一般是給系統用的。我們程序佔用的內存一般都是從0x100000000開始。

上面代碼的內存分佈,如下圖所示:
對象、指針 內存分佈
灰色內容表示內存單元的地址。黑色的表示內容。

0x03 再思考一下編程的本質

有了C語言的結構體,我們可以更好地管理內存,定義好一個結構體裏面的字段以及字段的類型,那整個結構體的大小就有了。當我們初始化一個結構體的時候(即OC中new一個對象),編譯器幫我們翻譯成彙編:開闢一段固定長度的內存。

我們再也不用在編程的時候思考:
我要開闢多少個內存單元?
我們要對這段內存的第幾個單元寫什麼值?(比如char類型’A’,ascII碼對應着65,需要給內存寫入65)

可見有了編譯器、有了語言,我們可以更方便的管理內存,控制邏輯。

我們編程的本質也從對CPU、寄存器、內存的控制,轉到了對數據類型、對象、邏輯的控制。

好了,就囉嗦這些。本人內功有限,如果有理解不到位的地方,強烈歡迎大家提出批評建議。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章