日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

objc@interface的设计哲学与设计技巧

發(fā)布時(shí)間:2025/7/14 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 objc@interface的设计哲学与设计技巧 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文原文發(fā)表自我的自建博客,cnblogs同步發(fā)表,格式未經(jīng)調(diào)整,內(nèi)容以原博客為準(zhǔn)。

?

我是前言

學(xué)習(xí)objc時(shí),尤其是先學(xué)過其他編程語言再來看objc時(shí),總會對objc的聲明的關(guān)鍵字interface感到有點(diǎn)奇怪,在其它面向?qū)ο蟮恼Z言中通常由class關(guān)鍵字來表示,而interface在java中表示的卻大約相當(dāng)于objc的protocol,這個(gè)關(guān)鍵字的區(qū)別究竟代表了objc語言的設(shè)計(jì)者怎樣的思想呢,在objc類設(shè)計(jì)中需要注意哪些問題呢?接下來對這個(gè)問題進(jìn)行一些思考和探究.


interface?

先來段Wiki:

In object-oriented programming, a protocol or interface is a common means for unrelated objects to communicate with each other. These are definitions of methods and values which the objects agree upon in order to cooperate.

接口約定了對象間交互的屬性和方法,使得對象間無需了解對方就可以協(xié)作。
說的洋氣點(diǎn)就是解耦嘛,細(xì)心點(diǎn)也能發(fā)現(xiàn)Wiki中interface和protocol表示了相近的語義。
引用我和項(xiàng)目組架構(gòu)師討論有關(guān)interface的問題時(shí)他的說法:

interface就是一個(gè)object定義的可以被外界影響的方式

說著他指了下旁邊桌子上放著的一把傘,說,這把傘我可以打開它,打開這個(gè)動作就是它的一個(gè)interface,桌子旁邊還放著一個(gè)盒子,雖然它和傘都放在這張桌子上,但是它們之間永遠(yuǎn)不會互相影響,所以:

interface只存在于能互相影響的兩者間


@interface生成了class?

學(xué)習(xí)objc時(shí)最早接觸的就是怎么寫一個(gè)類了,從.h中寫@interface聲明類,再從.m中寫@implementation實(shí)現(xiàn)方法,所以,objc中寫一個(gè)@interface就相當(dāng)于c++中寫一個(gè)class。但這是真的么?

寫個(gè)小test驗(yàn)證一下: 有兩個(gè)類,Sark和Dark,Sark類只有.m文件,其中只寫@implementation;Dark類只有.h頭文件,其中只寫@interface,然后如下測試代碼:

1 2 Class sarkClass = NSClassFromString(@"Sark"); Class darkClass = NSClassFromString(@"Dark");

NSClassFromString方法調(diào)用了runtime方法,根據(jù)類名將加載進(jìn)runtime的這個(gè)類找出來,沒有這個(gè)類就回返回空(Nil)。
結(jié)果是sarkClass存在,而darkClass為空,說明什么?是否說明其實(shí)@implementation才是真正的Class?
進(jìn)一步,不止能取到這個(gè)沒有@interface的類,還可以正常調(diào)用方法(因?yàn)槿f能的runtime)

如下面的測試代碼:

1 2 Sark *sark = [Sark new]; [sark speak];

要是沒有@interface的聲明,類名,方法名都會報(bào)錯(cuò)說找不到,但是可以像下面一樣繞一下:

1 2 3 Class cls = NSClassFromString(@"Sark"); id obj = [cls performSelector:NSSelectorFromString(@"new")]; [obj performSelector:NSSelectorFromString(@"speak")];

其實(shí),從rewrite后的objc代碼可以發(fā)現(xiàn),對于消息的發(fā)送,恰恰就是會被處理成類似上面的代碼,使用字符串mapping出Class,selctor等再使用objc_msgSend()進(jìn)行函數(shù)調(diào)用,如下面所示:

1 2 3 // 經(jīng)過clang -rewrite-objc 命令重寫后的代碼 Sark *sark = ((id (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Sark"), sel_registerName("new")); ((void (*)(id, SEL))(void *)objc_msgSend)((id)sark, sel_registerName("speak"));

對比@interface和@implementation

@interface?我們干過的事:

  • 繼承
  • 聲明協(xié)議
  • 定義實(shí)例變量(@interface后面加大括號那種)
  • 定義@property
  • 聲明方法
  • @implementation?我們干過的和可以干的事:

  • 繼承
  • 定義實(shí)例變量
  • 合成屬性(@synthesize和@dynamic)
  • 實(shí)現(xiàn)方法(包括協(xié)議方法)
  • 在@implementation干一些事情用的相對較少,但是是完全合法的,如這樣用:

    1 2 3 @implementation Sark : NSObject {NSString *_name; }

    通過對比可以發(fā)現(xiàn),@interface對objc類結(jié)構(gòu)的合成并無決定性作用,加上無決定性是因?yàn)槿绻麤]有@interface會丟失一些類自省的原始數(shù)據(jù),如屬性列表和協(xié)議列表,但對于純粹的對象消息發(fā)送并無影響。
    所以說,可以得出這么一個(gè)結(jié)論,objc中@interface就是為了給調(diào)用者看的,是和調(diào)用者的一個(gè)protocol,沒錯(cuò),就是protocol。

    對比@interface和@protocol

    與其把@implementation扯進(jìn)來不如對比下@protocol

    我理解objc的@interface和@protocal間唯一的區(qū)別就是是否和一個(gè)類型綁定,這讓我想起來鴨子類型(Duck typing),?wiki鏈接

    “當(dāng)看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子?!?/p>

    Duck type在objc的體現(xiàn)無疑就是@protocol了,我們常用id<XXXDelegate> delegate的方式聲明一個(gè)delegate,我們無需care這貨到底是什么類型,我們只知道他能干什么就可以work了。同樣的功能我也可以使用XXXDelegate *delegate的方式來定義,只不過這樣的話這個(gè)類又需要耦合一個(gè)XXXDelegate類型,而這個(gè)delegate類是它原本并不需要關(guān)心的。

    所以說,@interface是@protocol的強(qiáng)類型升級版。

    舉個(gè)NSObject的栗子最合適:

    1 2 3 @interface NSObject <NSObject> {Class isa; }

    NSObject之所以成為NSObject,絕大多數(shù)都是<NSObject>協(xié)議定義的方法,實(shí)體類@interface定義的唯一一個(gè)變量isa指針,為了繼承鏈和消息傳遞。
    除了<NSObject>協(xié)議外,NSObject還有很多Category來補(bǔ)充它的功能,其實(shí)仔細(xì)想想,Category更像protocol,一個(gè)補(bǔ)充協(xié)議,同樣不能添加實(shí)例變量,但是和@interface一樣需要與Class綁定。

    進(jìn)一步來講,自從屬性能自動合成變量之后,在頭文件@interface中寫大括號聲明實(shí)例變量的情況越來越少(可以參見近幾個(gè)版本iOS SDK中類頭文件里這種寫法幾乎消失),因此,@interface和@protocol的差別進(jìn)一步縮小。


    類與接口的設(shè)計(jì)原則 - 電視和遙控器

    我喜歡將Class和interface的關(guān)系比喻成電視+遙控器,那么objc中的消息機(jī)制就可以理解成:
    用戶(caller)通過遙控器(interface)上的按鈕(methods)發(fā)送紅外線(message)來操縱電視(object)
    所以,有沒有遙控器,電視都在那兒,也就是說,有沒有interface,class都是存在的,只是這種存在并沒有意義,就好像這個(gè)電視沒人會打開,沒人會用,沒人能看,一堆廢鐵擺在那兒。

    對比簡潔的遙控器,一個(gè)擁有很多按鈕的老式電視遙控器,我們經(jīng)常會用到的按鈕能有幾個(gè)呢?

    所以,在設(shè)計(jì)一個(gè)類的interface的時(shí)候,如同在設(shè)計(jì)遙控器應(yīng)該有怎樣功能的按鈕,要從調(diào)用者的角度出發(fā),區(qū)分邊界,應(yīng)該時(shí)刻有以下幾點(diǎn)考慮:

  • 這個(gè)方法或?qū)傩哉娴膶儆谶@個(gè)類的職責(zé)么?(電視遙控器能遙控空調(diào)?)
  • 這個(gè)方法或?qū)傩哉娴谋仨毞旁?h中(而不是放在.m的類擴(kuò)展中)么?
  • 調(diào)用者必須看文檔才能知道這個(gè)類該如何使用么?(同一個(gè)業(yè)務(wù)需要調(diào)用者按順序調(diào)用多次(而不是將這些細(xì)節(jié)隱藏,同時(shí)提供一個(gè)簡潔的接口)才行)
  • 調(diào)用者是否可以很容易發(fā)現(xiàn)類內(nèi)部的變量和實(shí)現(xiàn)方式?(腦補(bǔ)下電視里面一塊電路板漏在外面半截- -)
  • ...

  • objc的@interface設(shè)計(jì)技巧Tips

    看過不少代碼,從@interface設(shè)計(jì)上多少就能看出作者的水平,分享下我對于這個(gè)問題的一些拙見。

    只暴露外部需要看到的

    比如,有如下一個(gè)類(這個(gè)類無意義,主要關(guān)注寫法):

    1 2 3 4 5 6 7 8 // Sark.h @interface SarkViewController : NSObject <NSXMLParserDelegate /*1*/, NSCopying> {NSString *_name; // 2IBOutlet UITextField *_nameTextField; // 2 } @property (nonatomic, strong) NSXMLParser *parser; // 3 - (IBAction)nameChangedAction:(id)sender; // 4 @end

    這個(gè)interface出現(xiàn)的問題:

  • 類內(nèi)部自己使用的協(xié)議,如<NSXMLParserDelegate>不應(yīng)該在頭文件@interface中聲明,而應(yīng)該在類擴(kuò)展中聲明;公開由外部調(diào)用的協(xié)議,如<NSCopying>則寫在這兒是正確的。
  • 實(shí)例變量和IBOutlet不應(yīng)出現(xiàn)在這兒定義,這將類的內(nèi)部實(shí)現(xiàn)暴露了出去,自從屬性可以自動合成后,這里就更應(yīng)該清凈了。
  • 內(nèi)部使用的屬性對象不要暴露在外,應(yīng)該移動到類擴(kuò)展中。
  • 調(diào)用者對IBAction同樣不需要關(guān)心,那么就不應(yīng)該放在這兒。
  • 合理分組子功能

    • 將相同功能的一組屬性或方法寫在一起

    使用這個(gè)類或者對其進(jìn)行修改時(shí),一般都是從功能上找,所以把同一功能模塊的一組屬性或方法寫在一塊

    • 純操作方法的子功能(無需向類添加變量)使用Category分塊
    • 在頭文件中也可以使用類擴(kuò)展將interface按功能分區(qū)

    Category里不能添加實(shí)例變量,但是類擴(kuò)展可以,一般都在.m中作為私有interface使用,同樣在頭文件里作為分區(qū)使用,如,ReactiveCocoa中的RACStream.h

    避免頭文件污染

    首先,類實(shí)現(xiàn)內(nèi)部.m文件中使用的其他interface應(yīng)該在.m文件import,如果也寫在header中就會造成對調(diào)用者的污染;當(dāng)interface中出現(xiàn)其他Class或protocol時(shí),可以使用前置聲明@class XXX,?@protocol XXX;當(dāng)模塊(一組類)內(nèi)部間需要有一些定義(如常量、類型)而又不需要模塊使用者知道時(shí),使用一個(gè)內(nèi)部頭文件在模塊中使用。

    避免接口過度設(shè)計(jì)

    考慮調(diào)用者的使用方便是很必要的,過火了反而增加了復(fù)雜度:

    1 2 3 4 5 6 7 8 @interface Sark : NSObject - (instancetype)init; - (instancetype)initWithName:(NSString *)name; - (instancetype)initWithName:(NSString *)name sex:(NSString *)sex; - (instancetype)initWithName:(NSString *)name sex:(NSString *)sex age:(NSInteger)age; - (instancetype)initWithName:(NSString *)name sex:(NSString *)sex age:(NSInteger)age friends:(NSArray *)friends; // 無數(shù)多個(gè) // @end

    提供了一組這樣的方法,調(diào)用者可能只能用到其中的一個(gè),那這樣倒不如只留一個(gè)接口。

    避免單例的濫用

    單例模式固然好用,但感覺有點(diǎn)過度,將接口設(shè)計(jì)成單例入口前需要考慮一下:

  • 這個(gè)類表達(dá)的含義真的只能有一個(gè)實(shí)例么?(如UIApplication)還是只是為了好調(diào)用而已?
  • 這個(gè)單例持有的內(nèi)存一直存在
  • 是否能用類方法代替?
  • 這個(gè)單例對象是否能成為另一個(gè)單例對象的屬性?如果是,應(yīng)該作為屬性

  • 總結(jié)

    • @implementation合成了Class,而非@interface,@interface是@protocol的強(qiáng)類型升級版,它們和Category都表示了相近的含義
    • 我們應(yīng)該善于面向接口編程,劃清邊界,將類的實(shí)現(xiàn)隱藏在調(diào)用者所見之外,使主調(diào)和被調(diào)者之間保持最少知識原則
    • @interface本身就是最好的文檔

    References

    http://en.m.wikipedia.org/wiki/Interface_(object-oriented_programming)
    http://zh.wikipedia.org/wiki/%E9%B8%AD%E5%AD%90%E7%B1%BB%E5%9E%8B


    原創(chuàng)文章,轉(zhuǎn)載請注明源地址,blog.sunnyxx.com

    轉(zhuǎn)載于:https://www.cnblogs.com/sunnyxx/p/3674596.html

    總結(jié)

    以上是生活随笔為你收集整理的objc@interface的设计哲学与设计技巧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。