獨立開發者的自白:Objective-C最糟糕的13件事

csdn發表於2013-12-18

  本文的作者Anton Zherdev是一名具有多年開發經驗的獨立遊戲開發者,他從一個專業開發者的視角深入剖析Objective-C,將其與C、Java等其他語言相比,解讀Objective-C的優與缺,以最為精煉的話語總結出他所認為的Objective-C的13件最為糟糕的事,直指Objective-C的不足之處,與列位開發者分享。以下為文章全文:

  1. 笨重的語法

  在Objective-C中,必須要編寫大量的程式碼來宣告一個類或屬性。對比下面的Objective-C和Scala的程式碼段,效果不言而喻。

@interface Foo : NSObject
@property (readonly) int bar;
- (instancetype)initWithBar:(int)bar;
+ (instancetype)fooWithBar:(int)bar;
@end
@implementation Foo
- (instancetype)initWithBar:(int)bar {
   self = [super init];
   if (self) {
       _bar = bar;
   }
   return self;
}
+ (instancetype)fooWithBar:(int)bar {
   return [[self alloc] initWithBar:bar];
}
@end
class Foo {
   private int bar;
   public Foo(int bar) {
        this.bar = bar;
   }
   public int getBar() {
        return bar;
   }
}
class Foo(bar : Int) 

  2. 方括號

  字首括號是一件非常虐心的事情,儘管Objective-C有IDE自動解決這一問題,卻也大大降低了程式碼的可讀性。

  3. 記憶體管理

  與具有垃圾回收器的程式語言相比,Objective-C的記憶體更容易洩漏。雖然垃圾回收器可能會導致程式不定時回收,但也可通過設定來避免。在Objective-C中,儘管已經有ARC來解決這一問題,並且,開發者可以很好地利用它來解決一些弱引用的問題,卻還存在著無法解析引用週期的狀況。

  舉個例子,如果你在block中使用self,並將其傳送給另一個儲存該block的類,便會導致記憶體洩漏,而且還很難發現。並且,如果想要在類欄位中儲存block,就必須要將其copy過來,否則,當block被呼叫時,程式也就崩潰了。

  4. 動態且不安全的型別系統

  Objective-C有一個非常奇怪的型別系統。任何訊息,只要在檢視中可宣告,就可以將其傳送給id類物件,但卻無法迴避id。

@interface Foo : NSObject
- (void)foo;
@end
@implementation Foo
- (void)foo{}
@end
 
@interface Bar : NSObject
- (void)bar;
@end
@implementation Bar
- (void)bar {
  [(id)self foo]; //OK
  [(id)self bar]; //OK, but runtime error
  [(id)self qux]; //Compiler error
  Foo* foo = self; //OK
}
@end

  5. 不支援泛型

  在Objective-C中,要想檢視容器類所屬是不可能的,而且,編譯器也不能進行檢查。這方面,Java顯然要好得多。

NSArray* array = [foo array];
id item = [array objectAtIndex:0]; //item is anything
List<A> array = foo.getArray();
A item = array.get(0);

  6. 核心庫集匱乏

  在Objective-C的核心庫中,缺少諸如分類設定、字典(地圖)、連結串列、佇列等實用集。沒有它們,在進行紅黑樹分類和字典等開發管理時會花費大量的時間。

  還有一個問題就是缺乏幾項非常不錯的功能,尤其是函數語言程式設計能力有所缺失,儘管如此,但在Objective中,也有非常不錯的一項功能,就是開發者可以使用分類簡單地擴充套件核心集。

  7. 缺少列舉

  儘管Objective-C包含有C的列舉,但卻僅僅只是一組變數,開發者必須要編寫程式碼來實現一些類似於Java的列舉,比如連結屬性等。

enum Foo {
    Foo1(1),
    Foo2(2),
    Foo2(3);
    final int bar;
    Foo(int bar) {
        this.bar = bar;
    }
}

  8. 可怕的block語法

  block是Objective-C一項非常強大的功能,但我卻不知如何宣告帶有block型別的變數或函式引數。看下面這段程式碼便可知曉。

//Declare variable foo
void (^foo)(id obj, NSUInteger idx, BOOL *stop);

  9. 操作符過載缺失

  如果說無需操作符過載的話,難免有些欠妥,因為定義向量數學運算子是件很正常的事情,它使程式碼更具有可讀性。

[[[a add:b] sub:[c mul:f]] div:f];
(a + b - c*f)/f;

  10. 匿名類不足

  定義一個協議或介面並不像想象中那麼簡單,要想輕而易舉地實現,就必須要先實現一個完整的類。

@protocol I
- (void)f;
@end
 
@interface Foo : NSObject
- (void)foo;
- (void)qux;
@end
@implementation Foo
- (void)foo {
   id<I> i = [[Baz alloc] initWithFoo:self];
}
@end
 
@interface Baz : NSObject<I>
- (instancetype)initWithFoo:(Foo*)foo;
@end
@implementation B {
    Foo* _foo;
}
 
- (instancetype)initWithFoo:(Foo*)foo {
   self = [super init];
   if(self) {
   _foo = foo;
}
   return self;
}
 
- (void)f {
   [_foo bar];
}
@end
interface I {
   void f();
}
 
class Foo {
   void foo() {
       I i = new I() {
         void f() {
             bar();
         }
     };
  }
  void bar() {
  }
}

  11. 糟糕的建構函式

  使用建構函式建立新物件很常見,但在Objective-C中,要想建立物件,還需呼叫兩個函式。當然,開發者可以編寫方法直接避免該問題的發生。

@interface Foo : NSObject
- (instancetype)initWithBar:(int)bar;
+ (instancetype)newWithBar:(int)bar;
@end
@implementation Foo {
     int* _bar;
}
- (instancetype)initWithBar:(int)bar {
   self = [super init];
   if(self) {
   _bar = bar;
  }
  return self;
}
+ (instancetype)newWithBar:(int)bar {
   return [[self alloc] initWithI:bar];
}
@end

  12. 不透明的數值包裝器

  在Objective-C中,要想在集合或其他容器中使用數值是件很費勁的事情,除了原有程式碼之外,還需新增一個NSNumber類

int a = 1;
int b = 2;
int c = a + b;
NSNumber* aWrap = @(a);
NSNumber* bWrap = @(b);
NSNumber* cWrap = @([aWrap intValue] + [bWrap intValue]);

  13. 缺乏包管理系統

  在Objective-C中,開發者必須要使用到字首,以避免類名重合。此外,還要在標頭檔案中對所需類進行宣告,以防標頭檔案衝突,編譯失敗。

  來源:Anton Zherdev

相關文章