2010-05-12 29 views
6

Khi tôi có các định nghĩa xung đột về các màu của một lớp trong mục tiêu-c (không khai báo lại lớp trong cùng một tệp, mà là đặt tên cùng một lớp với các thẻ khác, không có cảnh báo hoặc lỗi nào được trình biên dịch phát hành. , cả hai bộ ivars là sử dụng được bằng các phương pháp thích hợp trong các tập tin tương ứng Ví dụTrình biên dịch GCC - lỗi hoặc hành vi không xác định?

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 

biên dịch mà không cảnh báo hoặc lỗi. Là ý định này? Đây có phải là lỗi không được kiểm tra không? (Đối với hồ sơ, a và baz thực sự là những vị trí bộ nhớ tương tự.)

Edit: cho các hồ sơ tôi đang nói về iPhone OS, mà tôi tin rằng sử dụng thời gian chạy giống như 64 MacOS chút

Trả lời

18

Mặc dù rõ ràng bị hỏng, mã đó nên biên dịch mà không cần cảnh báo trong mọi trường hợp đơn giản vì trình biên dịch không có đủ thông tin để biết cách cảnh báo. Khi được biên dịch một cách chính xác, nó tạo ra một lỗi liên kết hoàn toàn khác nhau chỉ trong 64-bit (đó là bụi phóng xạ từ Mục tiêu-C ABI mới, chứ không phải trực tiếp từ các ivars không mong manh).

Nếu bạn thêm int main() {} vào foo.m và sau đó biên dịch nó bằng dòng lệnh gcc -arch x86_64 foo.m -lobjc, lỗi liên kết sẽ biến mất vì thư viện runtime objc cung cấp biểu tượng vtable trống cần thiết để hoàn thành liên kết.

Trong quá trình biên soạn, hãy suy nghĩ từng tệp .m dưới dạng đơn vị biên dịch riêng biệt. Khi trình biên dịch biên dịch một tệp .m, nó chỉ có kiến ​​thức về những gì nằm trong tệp .m đó, những gì được cung cấp bởi bất kỳ thứ gì được nhập vào tệp .m đó và - nếu một dự án được cấu hình cho nó - những gì được định nghĩa trong tiêu đề được biên dịch trước của dự án.

Vì vậy, khi bạn nói trong bar.m:

@interface foo { 
float baz; 
} 

@end 

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

Trình biên dịch không có khái niệm về việc kê khai trong foo.m. Mã được tạo ra mô tả một danh mục trên lớp foo truy cập vào baz ivar. Nếu lớp đó không tồn tại ở thời gian liên kết, lỗi sẽ được gửi trong Hiện hành, với foo.m và bar.m của bạn khi tôi thêm một chức năng chính như trên, hãy thử một số cách biên dịch khác:

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 

Làm cho tinh thần bởi vì chúng tôi đã không xác định một hàm main() trong foo.m. 64 bit biên dịch không giống nhau.

gcc -arch i386 bar.m -lobjc 

Biên dịch và liên kết mà không cảnh báo. Để hiểu tại sao, nhìn vào những biểu tượng được tạo ra (xóa khoảng một chục người không liên quan):

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

Vì vậy, nhị phân chứa một loại tên category trên lớp foo. Không có lỗi liên kết vì trình liên kết không thực sự cố gắng giải quyết các danh mục. Nó giả định rằng lớp foo sẽ xuất hiện một cách kỳ diệu trước khi thể loại được giải quyết trong thời gian chạy.

Bạn có thể làm theo cùng với độ phân giải lớp/category của bộ thực thi với một Ivar:

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 

Vì vậy, hạng mục đã được đánh dấu là chưa giải quyết. Thời gian chạy sẽ treo nó ngay sau khi foo đi vào sự tồn tại!

Bây giờ, 64 bit ...

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 

Các lỗi liên kết là vì hiện đại Objective-C ABI thực sự gây ra những biểu tượng thích hợp để được phát ra cho các biến dụ và loại cho một loạt các lý do, bao gồm thêm siêu dữ liệu có thể giúp xác nhận các chương trình (như trong trường hợp này).

Không có lỗi biên dịch nào (hành vi đúng) và lỗi liên kết có ý nghĩa. Bây giờ, làm thế nào về liên kết hai với nhau?

Trong trường hợp 32 bit, mọi thứ biên dịch và liên kết mà không có lỗi. Vì vậy, chúng tôi sẽ cần phải nhìn vào các biểu tượng và tại spug gỡ lỗi ObjC để xem những gì đang xảy ra:

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) 

Aha! Bây giờ có một lớp foo và thời gian chạy kết nối danh mục với lớp khi khởi động. Rõ ràng, phương pháp trả lại là ngà baz sẽ thất bại một cách ngoạn mục.

Phiên bản 64 bit mối liên kết thất bại, mặc dù:

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 

Với việc bổ sung các biểu tượng cho các biến Ví dụ, các mối liên kết bây giờ có thể bắt các tình huống mà một lớp đã được redeclared không đúng cách (như đã được thực hiện trong @interface của men).

+0

vì vậy theo câu hỏi ban đầu của tôi: đây có phải là hành vi có chủ ý hoặc lỗi không được kiểm tra trên một phần của trình biên dịch không? Ngoài ra, đối với iPad, điều này đang được thử nghiệm trong thời gian chạy 'hiện đại' nhưng trên trình mô phỏng, có nghĩa là thực tế là i386, vì vậy nó tuân theo quy tắc 64 bit hoặc quy tắc 32 bit như được định nghĩa ở trên (khi bạn sử dụng các cụm từ này thay vì mới) thời gian chạy cũ) và quan trọng hơn, điều này có thay đổi trên thiết bị không? Và cuối cùng, khi quay trở lại baz ivar, không có sự thất bại ngoạn mục nào xảy ra vì nó là cùng một vị trí bộ nhớ như a, bằng chứng là * (int *) & baz cho giá trị của –

+0

Có thể và không biên dịch mà không cảnh báo. Nó sẽ gây ra lỗi liên kết trên trình biên dịch 64 bit trên máy tính để bàn và khi nhắm mục tiêu thiết bị, nhưng sẽ liên kết tốt (nhưng chạy sai) trong trình mô phỏng. Nếu bạn đang mong đợi để có thể lưu trữ một giá trị trong cả hai 'baz' và' a', nó sẽ thất bại một cách ngoạn mục, dưới thời gian chạy của trình giả lập, hai hiệu quả chia sẻ không gian lưu trữ. – bbum

0

Tôi nghĩ những gì bạn đã làm là extended lớp học.

Các vấn đề liên quan