2010-05-12 85 views
6

當我在objective-c中有一個類的ivars的定義衝突時(不是在同一個文件中重新聲明類,而是用diff ivars命名相同的類,編譯器不會發出警告或更好的錯誤。 ,高德的這兩組都在各自的文件中適當的方法可用例如GCC編譯器 - 錯誤還是未指定的行爲?

Foo.m:

@interface foo { 
int a; 
} 
- (int)method; 
@end 

@implementation foo 

- (int)method { 
    return a; 
} 

@end 

Bar.m:

@interface foo { 
float baz; 
} 

@end 

@implementation foo (category) 
- (float)blah { 
    return baz; 
} 
@end 

編譯沒有警告或錯誤。是這是故意的?這是一個未經檢查的錯誤? (備案,和巴茲實際上是相同的內存位置。)

編輯:對於我說的是iPhone OS,我相信使用相同的運行時間爲64位的MacOS

回答

18

雖然明顯被破壞,但是代碼應該在所有情況下編譯時都沒有警告,只是因爲編譯器沒有足夠的信息來知道如何警告。編譯正確時,它僅在64位(這是來自新的Objective-C ABI,而不是直接來自非易碎的ivars)中產生完全不同的鏈接器錯誤。

如果您將int main() {}添加到foo.m中,然後使用命令行gcc -arch x86_64 foo.m -lobjc進行編譯,則鏈接錯誤消失,因爲objc運行時庫提供了完成鏈接所需的空白vtable符號。

在編譯期間,將每個.m文件視爲一個獨立的編譯單元。當編譯器編譯一個.m文件時,它只知道該.m文件中的內容,該文件中導入的任何東西都提供了什麼,以及 - 如果爲其配置了項目 - 在該項目的預編譯頭文件。

因此,當你在bar.m說:

@interface foo { 
float baz; 
} 

@end 

@implementation foo (category) 
- (float)blah { 
    return baz; 
} 
@end 
int main() {} 

編譯器有沒有在foo.m.聲明的概念生成的代碼描述了foo類訪問ivar baz的類別。如果類沒有在鏈接時存在錯誤將在現在,我除了如上所述的主要功能給您foo.m和bar.m扔,讓我們嘗試一些不同的編譯:

gcc -arch i386 foo.m -lobjc 
Undefined symbols: 
    "_main", referenced from: 
     start in crt1.10.6.o 
ld: symbol(s) not found 
collect2: ld returned 1 exit status 

有意義,因爲我們沒有在foo.m中定義main()函數。 64位編譯也是一樣的。

gcc -arch i386 bar.m -lobjc 

編譯並沒有警告鏈接。要理解爲什麼,看所產生的符號(刪除大約有十幾個不相關的):

nm -a a.out 
00001f52 t -[foo(category) blah] 
00000000 A .objc_category_name_foo_category 

所以,二進制包含在foo類名爲category類別。沒有鏈接錯誤,因爲鏈接器實際上沒有嘗試解析類別。它假定類foo將在運行時解析類別之前奇蹟般地出現。

可以隨着運行時間的類/類別分辨率伊娃如下:

env OBJC_PRINT_CLASS_SETUP=YES ./a.out 
objc[498]: CONNECT: pending category 'foo (category)' 
objc[498]: CONNECT: class 'Object' now connected (root class) 
objc[498]: CONNECT: class 'Protocol' now connected 
objc[498]: CONNECT: class 'List' now connected 

因此,該類別被標記爲待定。只要foo成立,運行時就會將其掛起!現在

,64位...

gcc -arch x86_64 bar.m -lobjc 
Undefined symbols: 
    "_OBJC_IVAR_$_foo.baz", referenced from: 
     -[foo(category) blah] in ccvX4uIk.o 
    "_OBJC_CLASS_$_foo", referenced from: 
     l_OBJC_$_CATEGORY_foo_$_category in ccvX4uIk.o 
     objc-class-ref-to-foo in ccvX4uIk.o 
ld: symbol(s) not found 

的鏈接錯誤是因爲現代的Objective-C ABI實際上會導致發出適當的符號實例變量和類別的原因有多種,包括添加元數據這可以幫助驗證程序(就像在這種情況下那樣)。

沒有編譯錯誤(這是正確的行爲)和鏈接錯誤是有道理的。現在,將兩者聯繫起來怎麼樣?

在32位的情況下,一切都編譯和鏈接沒有錯誤。因此,我們需要看看符號和在ObjC調試嘔吐,看看發生了什麼:

gcc -arch i386 bar.m foo.m -lobjc 
nm -a a.out 
00001e0f t -[foo method] 
00001dea t -[foo(category) blah] 
00000000 A .objc_category_name_foo_category 
00003070 S .objc_class_name_foo 
env OBJC_PRINT_CLASS_SETUP=YES ./a.out 
objc[530]: CONNECT: attaching category 'foo (category)' 
objc[530]: CONNECT: class 'Object' now connected (root class) 
objc[530]: CONNECT: class 'Protocol' now connected 
objc[530]: CONNECT: class 'List' now connected 
objc[530]: CONNECT: class 'foo' now connected (root class) 

啊哈!現在有一個類foo,運行時啓動時將類連接到類。顯然,返回伊娃的方法將失敗壯觀。

64位鏈接失敗,雖然:

gcc -arch x86_64 bar.m foo.m -lobjc 
Undefined symbols: 
    "_OBJC_IVAR_$_foo.baz", referenced from: 
     -[foo(category) blah] in ccBHNqzm.o 
ld: symbol(s) not found 
collect2: ld returned 1 exit status 

由於增加了實例變量符號,鏈接器現在可以捕捉其中的一類已經被重新聲明不正確(如在的@interface完成情況bar.m)。

+0

所以根據我原來的問題:這是一個有意的行爲或編譯器的一部分未經檢查的錯誤?另外,對於iPad來說,這是在'現代'運行時測試,但在模擬器上進行測試,這意味着它實際上是i386,所以它會遵循上面定義的64位規則或32位規則(因爲您使用這些術語而不是新/舊的運行時),更重要的是,這將改變設備?最後,只要返回巴伐利亞伊阿爾去,沒有發生壯觀的失敗,因爲它是一個相同的內存位置,由*(int *)&baz產生的值的 – 2010-05-13 00:32:48

+0

該應該和沒有警告編譯。它會導致64位桌面編譯時的鏈接錯誤以及定位設備時出現鏈接錯誤,但會在模擬器中正常鏈接(但運行錯誤)。如果你希望能夠在'baz'和'a'中存儲一個值,它將會失敗,因爲在模擬器的運行時間下,這兩者有效地共享存儲空間。 – bbum 2010-05-13 03:17:17