淺談 OC 與結構體

引言

相信大家在 iOS 開發過程中都有過這樣的經歷, 當我們試圖對一個的控件單獨進行位置或大小修改的時候, 編譯器都會報錯, 使得我們不得不把控件的整個 frame 進行重新賦值:


1.png

在實際開發中我們一般會采用下面的做法, 俗稱 "3步曲" (通常會給 UIView 建一個分類封裝起來方便使用):


2.png

不知道大家可曾有過疑問, 為什么圖1中的 origin 和 size 不可以單獨賦值, 而圖2中的就可以呢? 帶著這個疑問我們一起來學習一下~

結構體的基本使用

我們知道無論是 CGPoint, CGSize 還是 CGRect 其本質都是結構體, 且它們存在著嵌套關系, CGPoint, CGSize 都是 CGRect 中的屬性:


3.png

我們平常做 iOS 開發的時候基本都是用 OC 語言(Swfit 先不談, 順帶一提 Swfit 中的結構體是一個很強大的存在), 很少會用到結構體(也可能是本人功力尚淺), 小弟我以前學 C 時有學到過, 但都忘得差不多了, 所以我們先來看看結構體的一些基本使用.

過于定義的東西網上一大把, 這里就不細說了, 其實 OC 中的類本質就是結構體, 只不過功能增強了很多, 所以我們可以簡單地把結構體理解為小類, 在結構體中我們可以定義屬性, 但不能定義方法.

我們先來定義一個簡單的結構體 Birthday , 里面有3個 int 類型的屬性(結構體中不能存放 OC 對象類型的屬性), 用來記錄與生日相關的3個信息:


4.png

接著我們創建了一個結構體變量 happy , 并對它作初始化. 結構體變量的初始化非常簡單, 直接在大括號里寫上對應的值就可以了, 跟 C 中定義數組的寫法一模一樣(順帶一提, 如果結構體中嵌套著結構體, 初始化時最外層也只用1對大括號包裹即可, 當然也可以在被嵌套的結構體對應的位置外多加1對大括號, 但千萬別加錯位置了, 否則會導致初始化失敗):


5.png

接下來要進入正篇部分了. 當我們想為結構體變量 happy 再次賦值時, 編譯器報錯了:


6.png

報錯是因為語法問題. 上面也提到了, 定義結構體與定義 C 中數組的寫法是一樣的, 所以直接把一個大括號賦值給一個變量系統并不能識別出這是一個數組賦值操作還是一個結構體賦值操作, 所以我們只要強轉一下即可:


7.png

當然我們也可以另外創建一個結構體變量 unhappy 初始化為我們想給 happy 修改成的值, 再把 unhappy 的值賦給 happy (因為 unhappy 也是結構體類型, 所以系統不會像上面一樣出現不能識別的情況):


8.png

另外如果我們只是想修改結構體變量中的某個值的話, 可以直接進行修改(訪問結構體變量中的屬性直接用我們最熟悉的 "." 語法即可. 當然, 如果結構體變量里嵌套著結構體變量, 想修改整個子結構體變量的話也是要用到上面所說的2種方法中的一種的. 如果想修改子結構體變量中的非結構體變量, 也是直接用 "." 語法來進行修改即可. 簡單來說就是, 非結構體變量可以直接修改, 結構體變量需要強轉或者通過另一個結構體變量來進行修改):


9.png

以上就是關于結構體的基本使用, 接下來要開始真正的正篇部分了~

當結構體作為屬性

當結構體作為類中的屬性來使用時, 又會擦出一些怎樣的火花呢? 接著我們一起來看一下.

首先我們新建一個 Person 類, 并在類中定義一個結構體, 出于環保的原則我們繼續延用上面的 Birthday 吧(當然之前的結構體定義已經不在了), 接著再給類中增加一個結構體屬性 happy :


10.png

然后我們在外面新建一個 Person 對象, 并試圖修改它的結構體屬性(結構體屬性在對象生成時已經被初始化了), 不出所料, 與上面例子中想修改結構體變量時所遇到的情況是一樣的:


11.png

接下來神奇的一幕出現了, 當我們想直接修改結構體屬性中的屬性時, 編譯器居然報錯了! 沒錯, 這個就是今天的重點了. 無論我是通過點語法還是通過 get 方法來獲取結構體屬性來修改其中的屬性都無效, 并且通過 get 方法來獲取結構體屬性那部分還比較清晰地說明了不能修改值的原因. 是的, OC 語法規定, 對象中的結構體屬性中的屬性是不允許作單獨修改的 . 這也解釋了引言中提出的一個疑問 --> "為什么圖1中的 origin 和 size 不可以單獨賦值, 而圖2中的就可以呢? " . 因為圖1中的 origin 和 size 是對象 view 中的結構體屬性 frame 中的屬性, 而圖2中的 origin 和 size 只是一個普通結構體變量中的屬性.

12.png

補充

如果你以為以上就是全部內容的話那你就錯了, 今天最壓軸的部分現在才開始(開玩笑啦, 其實主要的部分已經全部說完了, 一開始的疑問也得到了解釋, 已經算是圓滿收場了, 只是還有一點想補充的, 如果大家有時間也不妨來看看).

如果我告訴你, 上面得出的結論其實是錯誤的你會怎么想? 也就是說 對象中的結構體屬性中的屬性是不允許作單獨修改的 這句話其實是不正確的. 先不要生氣, 我并不是在自相矛盾, 聽我說完你就能理解了.

首先像剛才一樣, 我們新建一個 Person 類并在類中定義一個結構體 Birthday , 不同的是, 這一次我們不再寫 @property 屬性了, 而是直接添加屬性, 且為了能夠讓外部訪問, 加上 @public 關鍵字:


13.png

接著像之前的做法一樣, 在外面創建一個 Person 對象, 并且試圖修改其結構體屬性, 結果當然也是意料之中(此處訪問對象的屬性時用了 "->" 是因為 C語言 語法規定, 在通過指針來訪問結構體變量時, 若想訪問結構體變量中的屬性, 要用 "->" 來訪問, 這也從側面說明了 OC 中的類本質也是結構體):


14.png

來到這里, 可能你就郁悶了, 換了種定義的方式, 但也沒什么不一樣啊, 難道是特意為了說明 "->" 這個用法而來裝X的嗎? 先別急, 主角馬上要登場了. 還記得上面我說了哪個結論其實是不正確的嗎? 當我們試圖修改結構體屬性中的屬性時, 神奇的一幕又出現了:


15.png

怎么樣, 有沒感覺世界觀被刷新了? 不是說不能修改的嗎, 怎么現在又能修改了? 是的, 其實 對象中的結構體屬性中的屬性是允許作單獨修改的 , 不過前提是能直接拿到這個結構體屬性, 也就是說類要直接給外界暴露屬性, 但這是非常不符合面向對象語言中 封裝 特性的. 一般我們只會定義 @property 屬性, 相當于生成了私有屬性, 并且提供給外界 get 方法和 set 方法, 外界并不能直接拿到我們的屬性, 所以說在一般開發中, 對象中的結構體屬性中的屬性是不允許作單獨修改的 這句話雖然不正確, 但也能夠解釋大部分的問題了.

好了, 這次是真的結束了. 感謝大家能看到最后, 以上就是小弟的一些鄙見, 如果有說得不對的地方, 歡迎指正, 謝謝!
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容