2008-11-10 47 views
7

Tôi có hai lớp, một lớp con của lớp kia (nói AnimalDog). Lớp cha có một số bộ khởi tạo (ví dụ: initAnimal), lớp con có một số trình khởi tạo (ví dụ: initDog). Vấn đề là nó hợp pháp một cách phi pháp (từ quan điểm của trình biên dịch) để làm một cái gì đó như Dog *adog = [[Dog alloc] initAnimal], tức là. khởi tạo một lớp bằng cách sử dụng trình khởi tạo siêu lớp của nó. Tôi không thích điều này, bởi vì lớp con có thể có một số biến cá thể bổ sung mà tôi muốn chắc chắn được khởi tạo. Một cái nhìn vào tập tin tiêu đề giải quyết này, nhưng là có một cách đơn giản để làm cho trình biên dịch kiểm tra cho tôi? Tôi đã có một cảm giác tôi đang thiếu một cái gì đó khủng khiếp rõ ràng, nhưng tôi chỉ không thể đặt ngón tay của tôi vào nó :-)Khởi tạo một lớp bằng trình khởi tạo siêu lớp

Cập nhật: Các initDoginitAnimal không phải là ví dụ tốt nhất. Tôi có nghĩa là hai người khởi tạo khác nhau thực sự (như init cho AnimalinitWithFur cho Dog). Nếu tôi muốn mỗi con chó có một số lông được giao, tôi sẽ làm phần lông của bộ khởi tạo, để không ai có thể có được một vật thể con chó mà không có lông. Nhưng sau đó vẫn dễ nhầm lẫn khi khởi tạo phiên bản với siêu lớp init và sau đó tôi đã được sắp xếp.

Cảm ơn bạn đã đưa ra các trình khởi tạo được chỉ định, Jason. Nó đã không xảy ra với tôi trước đây, nhưng tôi có thể quá tải khởi tạo được chỉ định của lớp cha và đặt một số mặc định lành mạnh ở đó. Nhưng tôi vẫn thích nếu tôi bằng cách nào đó có thể làm cho nó bất hợp pháp để sử dụng initializers khác hơn so với những người khác của lớp học - bất kỳ ý tưởng nhiều hơn?

+0

Ok Zoul, tôi đã cập nhật câu trả lời của mình dựa trên các cập nhật cho câu hỏi của bạn ... nó đã khá lâu rồi, nhưng tôi hy vọng nó sẽ cung cấp cho bạn một số thông tin chi tiết mà bạn đang tìm kiếm. –

+0

Vì đây là một kết quả hàng đầu.Câu trả lời trong XCode hiện đại, bạn có thể đạt được điều đó với NS_UNAVAILABLE. Xem: http://stackoverflow.com/questions/195078/is-it-possible-to-make-the-init-method-private-in-objective-c#answer-27693034 – m4js7er

Trả lời

18

Nói chung trong mục tiêu-C bạn tạo một trình khởi tạo được chỉ định cho mỗi lớp và sau đó các lớp con sử dụng cùng một bộ khởi tạo. Vì vậy, thay vì sử dụng initAnimal và initDog, bạn chỉ cần sử dụng init. Các lớp con chó sau đó sẽ xác định phương pháp init riêng của mình và gọi initializer được chỉ định trong lớp cha của nó:

@implementation Dog 
-(id)init 
{ 
    if((self = [super init])) { // call init in Animal and assign to self 
     // do something specific to a dog 
    } 
    return self; 
} 
@end 

Bạn không thực sự phải ghi rõ initDog và initAnimal vì lớp được khai báo ở phía bên tay phải của phân công ...

cập nhật: tôi thêm dòng sau vào câu trả lời để phản ánh các thông tin bổ sung trong câu hỏi

có một số cách để đảm bảo rằng lớp con không gọi initializers khác hơn trình khởi tạo được chỉ định của họ và cách bạn tối ưu hóa ely chọn sẽ chủ yếu dựa trên toàn bộ thiết kế của bạn. Một trong những điều tốt đẹp về Objective-C là nó rất linh hoạt. Tôi sẽ cung cấp cho bạn hai ví dụ ở đây để giúp bạn bắt đầu. Trước tiên, nếu bạn tạo một lớp con có trình khởi tạo được chỉ định khác với lớp cha mẹ của nó, bạn có thể quá tải trình khởi tạo của cha và ném một ngoại lệ. Điều này sẽ cho phép các lập trình viên biết ngay lập tức rằng họ đã vi phạm giao thức cho lớp học của bạn ... tuy nhiên, cần phải nêu rõ rằng bạn cần có rất lý do chính đáng để làm điều này và cần phải ghi rõ rằng lớp con không sử dụng cùng bộ khởi tạo như lớp cha.

@implementation Dog 
-(id)init 
{ 
    // Dog does not respond to this initializer 
    NSAssert(false, @"Dog classes must use one of the designated initializers; see the documentation for more information."); 

    [self autorelease]; 
    return nil; 
} 

-(id)initWithFur:(FurOptionsType)furOptions 
{ 
    if((self = [super init])) { 
     // do stuff and set up the fur based on the options 
    } 
    return self; 
} 
@end 

Một cách khác để làm điều đó là khởi chạy giống như ví dụ ban đầu của bạn. Trong trường hợp đó, bạn có thể thay đổi init mặc định trên lớp cha để luôn thất bại. Sau đó bạn có thể tạo một bộ khởi tạo riêng cho lớp cha của bạn và sau đó đảm bảo mọi người gọi trình khởi tạo thích hợp trong các lớp con.Trường hợp này rõ ràng phức tạp hơn:

@interface Animal : NSObject 
-(id)initAnimal; 
@end 

@interface Animal() 
-(id)_prvInitAnimal; 
@end 

@interface Dog : Animal 
-(id)initDog; 
@end 

@implementation Animal 
-(id)init 
{ 
    NSAssert(false, @"Objects must call designated initializers; see documentation for details."); 

    [self autorelease]; 
    return nil; 
} 

-(id)initAnimal 
{ 
    NSAssert([self isMemberOfClass:[Animal class]], @"Only Animal may call initAnimal"); 

    // core animal initialization done in private initializer 
    return [self _prvInitAnimal]; 
} 

-(id)_prvInitAnimal 
{ 
    if((self = [super init])) { 
     // do standard animal initialization 
    } 
    return self; 
} 
@end 

@implementation Dog 
-(id)initDog 
{ 
    if((self = [super _prvInitAnimal])) { 
     // do some dog related stuff 
    } 
    return self; 
} 
@end 

Ở đây bạn thấy giao diện và triển khai lớp Thú và Chó. Động vật là đối tượng cấp cao nhất định và do đó ghi đè việc triển khai init của NSObject. Bất cứ ai gọi init trên một động vật hoặc bất kỳ lớp con của động vật sẽ nhận được một lỗi khẳng định giới thiệu chúng vào tài liệu. Animal cũng định nghĩa một initializer riêng trên một category riêng tư. Các thể loại tư nhân sẽ ở lại với mã của bạn và lớp con của động vật sẽ gọi này initializer tư nhân khi họ gọi lên đến siêu. Mục đích của nó là gọi init trên lớp cha của Animal (NSObject trong trường hợp này) và thực hiện bất kỳ khởi tạo chung nào có thể cần thiết.

Cuối cùng, dòng đầu tiên trong phương thức initAnimal của Animal là xác nhận rằng người nhận thực sự là một con vật và không phải là một số phân lớp con. Nếu người nhận không phải là một Động vật, chương trình sẽ thất bại với lỗi xác nhận và lập trình viên sẽ được chuyển đến tài liệu.

Đây chỉ là hai ví dụ về cách bạn có thể thiết kế một cái gì đó với các yêu cầu cụ thể của bạn. Tuy nhiên, tôi sẽ mạnh mẽ đề nghị bạn xem xét các ràng buộc thiết kế của bạn và xem liệu bạn có thực sự yêu cầu kiểu thiết kế này vì nó không chuẩn trong Cocoa và trong hầu hết các khung thiết kế OO. Ví dụ, bạn có thể xem xét việc tạo các đối tượng cấp độ động vật gốc khác nhau và chỉ cần có một giao thức Động vật thay thế, yêu cầu tất cả các "động vật" khác nhau phản ứng với một số thông điệp chung của động vật. Bằng cách đó, mỗi động vật (và các lớp con thực sự của Động vật) có thể xử lý các trình khởi tạo được chỉ định của chính chúng và sẽ không phải dựa vào các lớp bậc cao hoạt động theo một cách cụ thể, không chuẩn.

+0

Rất tốt, cảm ơn bạn đã dành thời gian . – zoul

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