iOS開發中很重要,很經常使用,但卻容易被忽略的知識點:id ,NSObject, id區別


咱們常常會混淆如下三種申明(我是沒有留意過):java

  • 1. id foo1;
  • 2. NSObject *foo2;
  • 3. id<NSObject> foo3;

    第一種是最經常使用,它簡單地申明瞭指向對象的指針,沒有給編譯器任何類型信息,所以,編譯器不會作類型檢查。但也由於是這樣,你能夠發送任何信息給id類型的對象。這就是爲何+alloc返回id類型,但調用[[Foo alloc] init]不會產生編譯錯誤。安全

  所以,id類型是運行時的動態類型,編譯器沒法知道它的真實類型,即便你發送一個id類型沒有的方法,也不會產生編譯警告。spa

  咱們知道,id類型是一個Objective-C對象,但並非都指向繼承自NSOjbect的對象,即便這個類型和NSObject對象有不少共同的方法,像retainrelease。要讓編譯器知道這個類繼承自NSObject,一種解決辦法就是像第2種那樣,使用NSObject靜態類型,當你發送NSObject沒有的方法,像length或者count時,編譯器就會給出警告。這也意味着,你能夠安全地使用像retainreleasedescription這些方法。代理

  所以,申明一個通用的NSObject對象指針和你在其它語言裏作的相似,像java,但其它語言有必定的限制,沒有像Objective-C這樣靈活。並非全部的Foundation/Cocoa對象都繼承息NSObject,好比NSProxy就不從NSObject繼承,因此你沒法使用NSObject*指向這個對象,即便NSProxy對象有releaseretain這樣的通用方法。爲了解決這個問題,這時候,你就須要一個指向擁有NSObject方法對象的指針,這就是第3種申明的使用情景。指針

   id<NSObject>告訴編譯器,你不關心對象是什麼類型,但它必須遵照NSObject協議(protocol),編譯器就能保證全部賦值給id<NSObject>類型的對象都遵照NSObject協議(protocol)。這樣的指針能夠指向任何NSObject對象,由於NSObject對象遵照NSObject協議(protocol),並且,它也能夠用來保存NSProxy對象,由於它也遵照NSObject協議(protocol)。這是很是強大,方便且靈活,你不用關心對象是什麼類型,而只關心它實現了哪些方法。對象

   如今你知道你要用什麼類型了不?繼承

   若是你不須要任何的類型檢查,使用id,它常常做爲返回類型,也常常用於申明代理(delegate)類型。由於代理類型一般在運行時,纔會檢查是否實現了那些方法。ip

   若是真的須要編譯器檢查,那你就考慮使用第2種或者第3種。不多看到NSObject*能正常運行,但id<NSObject>沒法正常運行的。使用協議(protocol)的優勢是,它能指向NSProxy對象,而更經常使用的狀況是,你只想知道某個對象遵照了哪一個協議,而不用關心它是什麼類型。編譯器