2009-08-14 36 views
10

Đây không phải là một câu hỏi kiểu. Nó nhiều hơn về việc sử dụng đúng ngôn ngữ. Tôi khá mới để lập trình và tôi hoàn toàn mới với Objective-C và Cocoa nhưng sau khi đọc về ngôn ngữ và xem xét một số mẫu mã một số mẫu sử dụng tiếp tục bật lên mà không có ý nghĩa với tôi. Hay đúng hơn là chúng dường như không tối ưu trong tâm trí tôi. Tôi hy vọng tất cả các bạn có thể giúp giáo dục tôi trong việc sử dụng đúng đắn một số cấu trúc ngôn ngữ này.Cách thích hợp để sử dụng Mục tiêu C

giao diện và giao thức

Tôi hiểu khái niệm về giao diện và triển khai khi chúng liên quan đến trừu tượng. Tuy nhiên mô hình chi phối tôi thấy trong mẫu mã Objective-C là sau ...

@interface Foo : NSObject 
{ 
    some ivars decls 
} 
some methods decls 
@end 

Và sau đó mã khách hàng theo hình thức ...

Foo* f = [[Foo alloc] init]; 

Điều này có vẻ kỳ quặc với tôi. Foo có vẻ là một thực hiện trong tâm trí của tôi (không quan tâm đến việc đặt tên không may của từ khóa @interface). Giao diện trong tâm trí của tôi không để lộ các biến mẫu. Một khách hàng của lớp này không nên được tiếp xúc với các chi tiết của việc thực hiện của tôi.

Mục tiêu-C có từ khóa @protocol mà trong đầu tôi có nhiều giao diện hơn từ khóa @interface. Nó cho phép bạn xác định các phương thức và/hoặc thuộc tính và đó là nó. Không thực hiện. Không có biến mẫu. Chỉ cần giao diện.

@protocol Foo <NSObject> 
    some methods 
    maybe some properties 
@end 

@interface FooProvider : NSObject 
    + (FooProvider*) sharedProvider; 
    - (id<Foo>) fooWithBlahBlah; 
@end 

Vì vậy, các mã khách hàng sẽ mang hình thức của ...

id<Foo> f = [[FooProvider sharedProvider] fooWithBlahBlah]; 
[f whatever]; 

Điều này có vẻ (ít nhất là với tôi) là một sử dụng trừu tượng hơn của ngôn ngữ và phân lập các khách hàng từ các chi tiết triển khai mà họ không phụ thuộc vào. Câu hỏi của tôi là tôi nên cố gắng làm theo. Tôi hiểu rằng có thể có những tình huống mà người ta thích hợp hơn người kia nhưng về mặt chung thì một người nên là tiêu chuẩn và một là ngoại lệ?

Một số hướng dẫn sẽ được đánh giá cao.

Cảm ơn, La Mã

Trả lời

10

tôi có thể nhìn thấy nơi bạn đang đến từ liên quan đến việc đặt tên của từ khóa @interface, nhưng Objective-C sử dụng hai từ khóa @interface@implementation để phân biệt giữa việc khai báo lớp (giao diện) và độ nét của nó (thực hiện). Phần @interface thường được đặt trong tệp tiêu đề lớp và phần @implementation nằm trong tệp nguồn. Tôi đồng ý với bạn 100% rằng các biến cá thể bên trong thực sự không nên xuất hiện trong tệp tiêu đề. Tôi không chắc điều gì đã dẫn đến quyết định đó, nhưng dự đoán tốt nhất của tôi là nó phải làm với kế thừa đối tượng. Khi bạn kế thừa từ một lớp cha mẹ:

#import "ParentClass.h" 
@interface MyClass : ParentClass 
... 

Bạn cũng thừa kế tất cả các biến thể hiện, trừ khi các biến đó được khai báo trong tiêu đề lớp.

Bạn cũng chính xác về cách sử dụng @protocol, trong đó về cơ bản nó xác định giao diện mà lớp phải tuân thủ. Các giao thức dường như là câu trả lời của Objective-C cho nhiều thừa kế.

Theo kinh nghiệm của tôi với Objective-C, các giao thức không được sử dụng thường xuyên. Họ có vị trí của họ, nhưng phần lớn mã sử dụng các từ khóa @interface@implementation cơ bản để xác định một lớp và giao thức chỉ được sử dụng khi một số chức năng bổ sung được yêu cầu không thuộc về hệ thống phân cấp lớp, nhưng vẫn cần phải được chia sẻ trên nhiều lớp.

Nếu bạn đang tìm kiếm để được hướng dẫn, tôi sẽ nói:

  1. Chấp nhận việc đặt tên của các từ khóa @interface@implementation, ngay cả khi nó không khá jive với những gì bạn mong đợi. Khi lập trình trong Objective-C, hãy uống Kool-Aid và xem những từ khóa đó theo cách mà các nhà thiết kế ngôn ngữ dự định.
  2. Sử dụng các giao thức khi chúng có ý nghĩa trong mã của bạn, nhưng đừng lạm dụng chúng trong một nỗ lực để làm cho Objective-C hoạt động giống như một ngôn ngữ khác. Nó có thể làm việc, nhưng những người khác sẽ rạn nứt khi họ nhìn thấy mã của bạn, và nó sẽ làm cho sự hợp tác trở nên khó khăn.
  3. Tiếp tục đặt câu hỏi hay. Bạn đã đưa ra một số điểm rất hợp lệ!
+0

Câu trả lời hay, đặc biệt là để giải quyết câu hỏi của OP về cá thể riêng các biến trong tệp tiêu đề. Chỉ cần thêm liên kết đến một câu hỏi SO khác về vấn đề cụ thể đó: http://stackoverflow.com/questions/966893/why-are-instance-variables-defined-in-the-header-file-in-objective-c –

+0

Mục tiêu -C là một lớp thực sự mỏng trên C. Nó thực sự chỉ quản lý một vài bảng biểu tượng trong quá trình biên dịch; rất giống với bản tiền xử lý c. Tôi chắc chắn hầu hết các quyết định đều làm cho việc viết mã trở nên dễ dàng hơn (như sử dụng dấu ngoặc vuông thay vì parens, họ phải tìm một thứ gì đó mà họ có thể dễ dàng phân tích cú pháp và trích xuất từ ​​mã xung quanh. Có lẽ lý do họ chọn tuân thủ cú pháp Lisp là Tôi nghĩ rằng các tập tin tiêu đề là đầu vào duy nhất, không chỉ là một thay thế tượng trưng, ​​vì vậy đó có lẽ là lý do tại sao họ đặt các biến ở đó –

+1

Họ không tuân theo cú pháp Lisp, mà là Smalltalk cố gắng xử lý càng nhiều càng tốt (bao gồm cả các câu lệnh điều kiện) thông qua việc sử dụng thông điệp đồng đều, và cú pháp phản ánh nguyên tắc thiết kế này bằng cách cơ bản bao gồm các khối lồng nhau – Felixyz

12

Trước tiên, bạn cần phải hiểu rằng bạn đang thực hiện một sai lầm kinh điển: đó là ngôn ngữ mà bạn đã biết định nghĩa các thuật ngữ được sử dụng trong tất cả các ngôn ngữ - như thể một ngôn ngữ cụ thể đã thiết lập một kinh điển danh pháp.

Java đã được bắt đầu vào năm 1990 trong nội bộ tại Sun. Mục tiêu-C đã được phát hành vào năm 1986. Vì vậy, nếu có, thuật ngữ giao diện của Java là mâu thuẫn với Mục tiêu-C, không phải theo cách khác. Tuy nhiên, giao diện có lịch sử lâu hơn nhiều so với một trong các ngôn ngữ này.

Ý tưởng đằng sau @ interface trong Objective-C được nêu trong Objective-C 2.0 Programming Language Reference:

giao diện Một phần của một đặc tả lớp Objective-C khai báo public interface của nó, bao gồm tên lớp cha của nó, ví dụ biến, và nguyên mẫu phương pháp công khai.

phương pháp thể hiện là phương thức lớp là công khai nếu chúng được khai báo trong giao diện. nếu chúng được khai báo trong @implementation thì chúng là riêng tư.

Việc triển khai sẽ diễn ra trong quá trình thực hiện. Có lẽ điều làm bạn khó hiểu là Java không có khái niệm về phần khai báo lớp như trong Objective-C. Khai báo lớp khai báo giao diện lớp theo cách tổng quát, không có chi tiết cụ thể về việc triển khai thực hiện. @implementation có triển khai thực hiện thực tế, là chi tiết về cách lớp @interface được triển khai.

Không sai khi mục tiêu-c khác với Java, nó chỉ khác nhau.

Nhưng bạn nói đúng, giao thức là tương tự gần nhất với giao diện java trong mục tiêu-c bạn sẽ tìm thấy. Nó không phải là một siêu lớp cho bất cứ điều gì, và không có bất kỳ thực hiện liên quan. Bạn cung cấp giao thức nhiều như bạn sẽ giao diện trong Java.

Tuy nhiên, lưu ý rằng các giao thức không phổ biến như các lớp trong Mục tiêu-C. Điều đó sẽ giúp hướng dẫn suy nghĩ của bạn.

+0

Tôi xin lỗi nếu tôi xuất hiện khi so sánh Objective-C với Java. Đó không phải là ý định của tôi. Từ Java không bao giờ xuất hiện trong bài viết gốc của tôi. Tôi chỉ đơn giản là nói về xung đột nội bộ của tôi về những gì tôi coi là một giao diện và những gì Objective-C coi là một giao diện. Lời khuyên của việc chỉ làm những gì các mã khác không chính xác ngồi với tôi nội bộ nhưng nó có một số công đức. Khi ở trong rome vv ... Hãy cho tôi một cái gì đó để suy nghĩ về. –

+2

Vâng, người dùng của bạn về thuật ngữ "giao diện" theo cách Java sử dụng nó cho thấy một sự hiểu biết dựa trên thuật ngữ và khái niệm của Java, nhưng không nhất thiết phải hợp lệ bên ngoài khung khái niệm của Java. Học ngôn ngữ là một bài tập cực kỳ hữu ích - học cách đánh giá cao sự khác biệt về trí tuệ để thông báo cho bạn sử dụng các ngôn ngữ khác. – groundhog

+1

(Tôi không nghĩ rằng người hỏi đã trực tiếp rỗ Obj-C chống lại Java, nhưng bạn đã chính xác rằng có một số âm bội gợi ý nhầm lẫn do sử dụng thuật ngữ trong hai ngôn ngữ.) Objective-C đã thực sự được phát minh vào năm 1982, và tôi thấy nó rất thú vị khi tôi phát hiện ra rằng Objective-C có ảnh hưởng không tầm thường trên Java - http://www.virtualschool.edu/objectivec/influenceOnJava.html –

3

Không ai giải thích tại sao các biến mẫu xuất hiện trong lớp @interface của lớp Objective-C. Các đối tượng được thực hiện như các cấu trúc C chứa các biến cá thể. Khi bạn tạo một lớp con mới, một kiểu cấu trúc mới được định nghĩa với tất cả các dấu ba chấm của siêu lớp tiếp theo là các thanh phân lớp của lớp mới. Cấu trúc siêu lớp phải chứa các trụ của lớp con của nó là, và sau đó là "rùa tất cả các con đường xuống" đến một cấu trúc đại diện cho các ivars của lớp cơ sở.

Trong trường hợp của NSObject (và thực sự Object), cấu trúc lớp cơ sở chỉ chứa một Ivar - một con trỏ được gọi là isa đến Class cấu trúc đại diện cho lớp của đối tượng này. Vì vậy, nếu tôi muốn phân lớp nó như thế này:

@interface GLObject : NSObject { 
    int x; 
} 
@end 

tôi cần phải biết làm thế nào để tạo ra một cấu trúc trông giống như:

{ 
    Class isa; 
    int x; 
} 

do đó bố trí Ivar của lớp cha mẹ (và bằng cảm ứng mọi lớp) cần phải là một phần của hợp đồng công khai của lớp học, tức là một phần của @interface của lớp học. Nó có thể không đẹp, nhưng có nó.

+2

Trong thực tế, điều này không còn đúng trong thời gian chạy không mong manh (64-bit Mac OS X và iPhone), không sử dụng bố trí cố định. Khi xây dựng cho thời gian chạy không mong manh, bạn có thể ngầm thêm ngà vào @implementati về việc sử dụng chỉ thị @synthesize. Không có lý do kỹ thuật nào để không cho phép {} khối ivar trong @implementation; từ là điều này đã được bỏ ra hoàn toàn vì lý do thời gian. Lỗi đã được nộp. –

+0

Đúng, quá đúng. Không có sự cần thiết về mặt kỹ thuật vì @interface được sử dụng để thông báo cho trình biên dịch về các loại và thành viên cần thiết khi biên dịch nguồn tham chiếu đến lớp mà không gây ra sự phụ thuộc vào việc triển khai thực hiện. Nếu một ivar là @private, không có lý do thuyết phục thực sự nào cho sự tồn tại của nó trong khối @interface ngoài cú pháp. – groundhog

+0

@both nhận xét: đúng. Tôi nghĩ rằng giới thiệu nhiều runtimes vào thời điểm này không phải là khá thích hợp ;-) –

1

Tôi cũng đã nhận ra không có gì thực sự buộc phải đối mặt với các giao diện công cộng của tôi để chứa các biến dụ là ...

@interface Foo : NSObject 
// some methods but no ivars 
+ (Foo*) fooWithBlahBlah 
@end 

Và các tập tin thực thi ...

@implementation Foo 

+ (Foo*) fooWithBlahBlah { 
    return [[SomeDerivedFoo alloc] init]; 
} 

@end 

đâu SomeDerivedFoo được khai báo nội bộ không hiển thị với khách hàng ...

@interface SomeDerivedFoo : Foo 
{ 
    // instance vars 
} 
// overrides of methods 

Điều này sẽ cho phép thành phần để lộ một giao diện không có các phụ thuộc ivar. Trong thực tế, đây là mô hình mà tôi thấy trong cái gọi là "Class Class". Điều này "cảm thấy" rất nhiều tốt hơn cho tôi.

Vấn đề lớn của tôi với ivars trong giao diện công cộng là tất cả về kiểm soát phụ thuộc. Nếu các ivars của tôi không là gì ngoài các lớp Objective-C khác thì tôi có thể loại bỏ các khai báo về phía trước nhưng trong trường hợp các cấu trúc C được nhúng hoặc các kiểu không phải lớp khác, tôi phải xem định nghĩa của các kiểu đó và vì vậy khách hàng của tôi bây giờ phụ thuộc vào chúng.

Thay đổi biến mẫu không được khiến khách hàng của giao diện của tôi biên dịch lại. Một relink là tốt nhưng không phải là một biên dịch lại. Trong trường hợp của một khuôn khổ chia sẻ thậm chí không phải là một relink nên là cần thiết. Điều này không liên quan gì đến bất kỳ ngôn ngữ cụ thể nào. Nó phải làm với sự cô lập từ các chi tiết. Đó là một mục tiêu khá phổ quát trong đầu tôi.

Đây là những gì đã dẫn đến câu hỏi ban đầu của tôi về giao thức so với giao diện. Dường như đối với một thư viện hoặc khung công tác, điều "đúng" hơn là cung cấp một bộ các giao thức cùng với một số loại "Nhà cung cấp" hoặc "Nhà máy" để thực hiện các giao thức đó.

1

Một giải pháp thay thế nếu bạn không thể hỗ trợ thời gian chạy 64 bit và chỉ tổng hợp ivars, là tạo một thanh ngang id trong lớp học của bạn. Nếu bạn cần trở lại lớp học của mình sau khi giao hàng và thêm các biến mẫu khác, bạn có thể sử dụng số này id làm vùng chứa cho chúng.

Điều này cho phép bạn viết mã ít phức tạp hơn, trong khi cho bạn một số thay đổi để thay đổi mọi thứ phía sau lưng của khách hàng mà không cần phải biên dịch lại.

Bằng cách nhập mã vạch là id, bạn không hứa hẹn bất kỳ điều gì đối với mã máy khách (dù sao nó cũng phải được làm @private).

Tôi sẽ hình dung rằng tất cả các lớp công khai của bạn hoạt động như proxy để triển khai cá nhân sẽ có một chút không thể quản lý được. Hãy ghi nhớ, nếu bạn làm thế, bạn có thể sử dụng chuyển tiếp thời gian chạy để giảm số lượng mã bạn phải viết trong lớp công khai. Xem -[NSObject forwardingTargetForSelector:]. Tài liệu được ghi lại trong số 10.5 Foundation Release Notes

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