2010-08-27 18 views
5

Tôi đã thử mã này:Điều gì cần thiết để triển khai lớp gốc của Mục tiêu-C?

// main.m 
#import <stdio.h> 

@interface Test 
+ (void)test; 
@end 
@implementation Test 
+ (void)test 
{ 
    printf("test"); 
} 
@end 

int main() 
{ 
    [Test test]; 
    return 0; 
} 

với LLVM/Clang không có khung nào, nó không được biên soạn với lỗi này:

Undefined symbols: 
    "_objc_msgSend", referenced from: 
     _main in main.o 
ld: symbol(s) not found 
clang: error: linker command failed with exit code 1 (use -v to see invocation) 

Vì vậy, tôi đã thêm libobjc.dylib. Mã đã được biên dịch, nhưng đã ném ngoại lệ thời gian chạy này:

objc[13896]: Test: Does not recognize selector forward:: 
Program received signal: “EXC_BAD_INSTRUCTION”. 

#0 0x9932a4b4 in _objc_error 
#1 0x9932a4ea in __objc_error 
#2 0x993212b6 in _objc_msgForward 
#3 0x99321299 in _objc_msgForward 
#4 0x99321510 in _class_initialize 
#5 0x99328972 in prepareForMethodLookup 
#6 0x99329c17 in lookUpMethod 
#7 0x99321367 in _class_lookupMethodAndLoadCache 
#8 0x99320f13 in objc_msgSend 
#9 0x00001ee5 in start 

Tôi đã thực hiện một số yêu cầu đối với lớp gốc nhưng tôi không biết phải làm gì tiếp theo. Điều gì cần thiết để tạo một lớp gốc mới? Và có bất kỳ đặc điểm kỹ thuật cho điều này?

+0

Tôi tìm thấy một nguồn tài nguyên có giá trị: http://code.google.com/p/ purefoundation/ Tuy nhiên, vẫn không biết về các thông số kỹ thuật – Eonil

+0

Chỉ để tham khảo sau này, nguồn chạy objc nguồn mở của Apple chứa nguồn 'NSObject' quá – Eonil

Trả lời

6

Tôi chỉ đến câu hỏi này bởi vì tôi đã có cùng một câu hỏi "học thuật". Sau khi làm việc thông qua nó một chút, tôi đã thấy rằng các câu trả lời khác cho câu hỏi này không hoàn toàn chính xác.

Đúng là trên thời gian chạy của Apple Objective-C 2.0, bạn phải triển khai một số phương pháp để mã của bạn hoạt động.Thực ra chỉ có một phương thức mà bạn cần thực hiện: phương thức lớp initialize.

@interface MyBase 
+ (void)test; 
@end 
@implementation MyBase 
+ (void)initialize {} 
+ (void)test { 
// whatever 
} 
@end 

Bộ thực thi sẽ tự động gọi initialize khi bạn lần đầu tiên sử dụng lớp học của bạn (như đã giải thích in Apple's documentation). Không triển khai phương pháp này là lý do cho lỗi chuyển tiếp tin nhắn.

Biên dịch với clang test.m -Wall -lobjc (hoặc gcc) sẽ cho phép bạn gọi thử nghiệm phương thức lớp mà không gặp bất kỳ sự cố nào. Làm công việc phân bổ đối tượng là một câu chuyện khác. Ít nhất, bạn sẽ cần một con trỏ isa trên lớp cơ sở của bạn nếu bạn đang sử dụng các biến mẫu. Thời gian chạy hy vọng điều này sẽ ở đó.

+0

Bạn nói đúng. Nó hoạt động! Cảm ơn, đây là một bước tiến lớn đối với tôi :) – Eonil

2

Trên runtime của Apple, các thông số kỹ thuật tối thiểu là khá dễ dàng giải thích: Bạn phải thực hiện mỗi phương pháp trong giao thức NSObject. Xem here. Điều này hoàn toàn là không tầm thường. Bạn có thể muốn thêm một vài chức năng bổ sung như +alloc để có thể tạo một cá thể, v.v. Chỉ có hai lớp gốc công khai trong tất cả các khung công tác của Apple: NSObjectNSProxy. Trong thực tế, hoàn toàn không có lý do gì để tạo ra một lớp gốc. Tôi không chắc là có bất kỳ tài liệu nào về vấn đề này của Apple.

Những gì bạn sẽ làm trong thực tế là kế thừa từ NSObject hoặc NSProxy và xây dựng trên đầu trang của chúng. Mã của bạn sẽ hoạt động nếu bạn thực hiện như sau:

@interface Test : NSObject 
+ (void)test; 
@end 

Như Tilo đã chỉ ra, đây không phải là trường hợp trên các thời gian chạy khác như thời gian chạy GNU.

+0

Bạn đang ở bên phải Max: Triển khai một lớp gốc mới là một thử thách và có thể thú vị Theo quan điểm của một viện nghiên cứu, nhưng thực tế chỉ có rất ít ứng dụng – GorillaPatch

+0

Trong thực tế, Apple đã tạo ra chỉ 5 lớp gốc trong tất cả các khuôn khổ Cocoa, bao gồm nsobject và nsproxy.Tôi chưa bao giờ có nhu cầu tự tạo. –

+0

Nó chỉ là một loại thử nghiệm làm chương trình objc trên nền tảng không phải của Apple (như FreeBSD). Tôi đồng ý với không có lý do gì để tạo lớp gốc của riêng tôi trong nền tảng của Apple :) – Eonil

1

Ví dụ trên biên dịch tốt với gcc và thời gian chạy GNU. Trong Objective-C thông thường bất kỳ lớp nào cũng có thể là một lớp gốc bằng cách đơn giản là không có một lớp siêu. Nếu thời gian chạy của Apple yêu cầu một cái gì đó khác nhau, thì đó là thời gian chạy cụ thể.

Ngoài ra có cái gì đó cụ thể với các lớp gốc:

Tất cả các phương pháp dụ cũng là phương pháp lớp học với việc thực hiện tương tự (nếu không được thực hiện một cách rõ ràng cách khác). Đầu ra của ứng dụng sau:

#import <stdio.h> 

@interface Test 
+ alloc; 
+ (void)test; 
- (void)test; 
- (void)otherTest; 
@end 
@implementation Test 
+ alloc 
{ 
    return (id)class_create_instance(self); 
} 

+ (void)test 
{ 
    printf("class test\n"); 
} 

- (void)test 
{ 
    printf("instance test\n"); 
} 

- (void)otherTest 
{ 
    printf("otherTest\n"); 
} 
@end 

int main() 
{ 
    id t = [Test alloc]; 
    [Test test]; 
    [t test]; 
    [Test otherTest]; 
    [t otherTest]; 
    return 0; 
} 

sẽ là:

class test 
instance test 
otherTest 
otherTest 

Phần khó khăn nhất về việc tạo ra một lớp rễ mới là +alloc-dealloc nhưng như đã thấy trong ví dụ của tôi thời gian chạy (trong trường hợp của tôi GNU-runtime) có thể thực hiện điều này. Nhưng tôi không biết liệu phân bổ thời gian chạy có đủ tốt hay không. Tôi biết rằng một số nền tảng sử dụng một cơ chế phân bổ riêng để ẩn bộ đếm tham chiếu khỏi cấu trúc đối tượng. Tôi không biết nếu Apple làm điều này quá và nếu thời gian chạy của họ đã chăm sóc nó.

2

Trong hệ thống của tôi (GNUstep trên linux + GCC), tôi đã phải thay thế phương thức phân bổ ở trên bằng cách sau đây, để thực hiện công việc mẫu. Tôi nghĩ rằng điều này là do một mới hơn obj-c runtime (tài liệu của bộ thực thi ở đây:. Objective-C Runtime Reference từ Thư viện nhà phát triển Mac

+ alloc 
{ 
    return (id)class_createInstance(self, 0); 
} 
Các vấn đề liên quan