2009-08-06 32 views
24

Tôi đang sử dụng một khuôn khổ xác định và sử dụng 'ClassA', một phân lớp của NSObject. Tôi muốn thêm một số biến và chức năng vì vậy tự nhiên tôi đã tạo 'ClassB', một lớp con của 'ClassA'Làm thế nào để bỏ 'Lớp A' vào phân lớp 'Lớp B' - Mục tiêu-C

Bây giờ vấn đề của tôi là vấn đề này. Nhiều phương thức trong khuôn khổ này trả về các cá thể của 'ClassA' mà tôi muốn chuyển sang lớp con của tôi.

Ví dụ lấy phương pháp này:

- (ClassA *)doSomethingCool:(int)howCool 

Bây giờ trong mã của tôi, tôi cố gắng này:

ClassB * objB; 
objB = (ClassB *)doSomethingCool(10); 

NSLog(@"objB className = %@", [objB className]); 

này chỉ chạy tốt. Không có lỗi biên dịch hoặc thời gian chạy hoặc bất kỳ điều gì. Nhưng điều thực sự lạ với tôi là đầu ra:

>> "objB className = ClassA" 

Việc truyền hiển nhiên không thành công. Không chắc chắn những gì đã xảy ra vào thời điểm này ... objB được đánh máy là 'ClassB', nhưng className là 'ClassA' và nó sẽ không trả lời bất kỳ phương thức 'ClassB' nào.

Không chắc chắn về điều này có thể xảy ra ... Bất kỳ ai biết tôi đang làm gì sai ở đây?

Tôi tìm thấy một bài tương tự đó là hoàn toàn ngược lại những gì tôi đang hỏi here

+0

Bạn có thể cho biết cách bạn đã tạo ClassB trong doSomethingCOol không? – stefanB

Trả lời

22

Biến đối tượng đúc trong Mục tiêu-C thường là một sai lầm (có một vài trường hợp mà nó đúng, nhưng không bao giờ cho loại điều này). Lưu ý rằng bạn không truyền một đối tượng - bạn đang tạo một con trỏ cho một đối tượng. Sau đó bạn có một con trỏ kiểu ClassB*, nhưng nó vẫn trỏ đến cùng một cá thể của ClassA. Điều được chỉ ra không hề thay đổi.

Nếu bạn thực sự muốn chuyển đổi các thể hiện của ClassA thành ClassB, bạn sẽ cần phải viết một phương thức khởi tạo ClassB có thể tạo một cá thể ClassB từ một ClassA. Nếu bạn thực sự cần thêm các biến mẫu, thì đây có thể là lựa chọn tốt nhất của bạn.

Như Jason đã nói, tuy nhiên, bạn nên thử một danh mục trước tiên.

+0

Hãy nhớ rằng trường hợp của 'ClassA' được trả về bởi phương thức này có thể _really_ là một đối tượng kiểu' ClassB', mà _will_ trả lời các thông điệp được hiểu bởi các đối tượng 'ClassB'. – mipadi

+0

Mặc dù bây giờ tôi đã đọc lại câu hỏi, không chính xác trong kịch bản này, vì nick dường như đang sử dụng một khung bên thứ ba mà không biết về 'ClassB'. – mipadi

+0

Ngoài ra, bất kể nó là gì, đó là một lỗi lập trình để giả định nó là bất cứ điều gì khác với những gì nó được gõ. Ngoại lệ duy nhất cho việc này là các API trong đó các kiểu cơ sở (như id hoặc NSObject) được sử dụng cụ thể vì người gọi có nghĩa là biết chính xác loại đối tượng sẽ là (hoặc chịu trách nhiệm tìm ra theo chương trình) - như trong bộ sưu tập và tin nhắn hành động. –

18

Bạn không thể chỉ đúc một lớp siêu để lớp con của nó. Nó không thực sự triển khai bất kỳ biến/thuộc tính hoặc phương thức nào được thêm vào của bạn. Ngay sau khi bạn cố gắng nhắn tin nó với một phương thức bạn xác định trên lớp con của bạn, bạn sẽ nhận được một ngoại lệ thời gian chạy và ứng dụng của bạn sẽ thoát. Bạn chỉ có thể đúc một cách an toàn theo một hướng: từ cụ thể hơn đến tổng quát hơn. Tức là, bạn có thể bỏ ClassB của chúng tôi sang ClassA một cách an toàn, chỉ sử dụng các phương pháp và thuộc tính của ClassA, nhưng không phải là cách khác.

Hãy suy nghĩ theo cách này: bạn có Xe hơi (lớp cha) và FourDoorSedan (phân lớp). Mỗi chiếc xe đều có động cơ và hai cửa. Bây giờ, giả sử bạn đang nhận được một chiếc xe hơi từ đâu đó, và đó thực sự là tất cả những gì bạn biết về nó. Bạn nói với các nhà điều hành, chiếc xe đó là một FourDoorSedan, nhưng trên thực tế nó quay ra nó không phải. Vì vậy, khi các nhà điều hành làm một cái gì đó như: openBackPassengerDoor, những gì sẽ xảy ra? Chỉ có hai cánh cửa !! Ở đây cũng tương tự.

Nếu bạn chỉ muốn thêm một chức năng nhỏ vào ClassA, hãy xem Mục tiêu-C Danh mục, chúng có thể là những gì bạn muốn và không cần truyền. Tuy nhiên, đọc tài liệu một cách cẩn thận, vì chúng không phải không có sự cẩn thận của chúng.

+4

Bạn có thể cast nếu 'doSomethingCool:' thực sự _did_ trả về một đối tượng kiểu 'ClassB'. Kiểu trả về của nó là 'ClassA', nhưng nó thực sự có thể trả về một đối tượng' ClassB' (vì 'ClassB'" là-một "' ClassA'). Nếu bạn _know_ 'doSomethingCool:' trả về một đối tượng kiểu 'ClassB' (mà bạn có thể kiểm tra bằng cách sử dụng' [objB isKindOfClass: [ClassB class]] 'nếu bạn không chắc chắn), thì an toàn khi truyền và gửi một thông điệp để 'objB' nói nó thực hiện phương thức' ClassB'. Bạn cũng có thể sử dụng 'respondsToSelector' để chắc chắn là _really_. – mipadi

+0

Mặc dù, đọc lại câu hỏi, nếu khung là khung bên thứ ba không biết gì về 'ClassB' (và chưa được sửa đổi bằng nick) thì nó sẽ không trả về các đối tượng thực sự là trường hợp của 'ClassB' anyway. – mipadi

+0

Chính xác, đó là khung bên thứ 3 nên chắc chắn không trả lại CLassB. Ngoài ra, chúng ta biết * chắc chắn * rằng nó không trả về ClassB vì [objB class] đang trả về ClassA. –

0

Điều gì sẽ xảy ra khi bạn gọi một trong các phương thức phân lớp của bạn trên đối tượng được trả về?

ClassB * objB; 
objB = (ClassB *)doSomethingCool(10); 
[objB subClassMethod]; 

Obj-C là khá miễn phí và bị mất với các loại, bạn thậm chí không cần phải biết loại trước đó, ví dụ: bạn có thể thay đổi tất cả các tham chiếu ClassB thành id. Tôi biết rằng an toàn hơn để biết bạn có loại mong đợi nhưng nếu mã của bạn chính xác thì nó không quan trọng.

+0

Mã của anh ấy không chính xác. Anh ấy nhận được một ClassA và chỉ giả vờ là một ClassB vì anh ấy thích ClassB. Đó là lý do tại sao bạn không thể tự ý upcast một cái gì đó. Trong ví dụ của bạn, ứng dụng của anh ta sẽ gặp sự cố trên dòng 3. –

+0

Tôi cho rằng anh ta đã vượt qua ClassB của mình ở một nơi nào đó và khung công tác đã chuyển nó trở lại cho anh ta trong trường hợp đó mã sẽ ổn. Nếu đây không phải là trường hợp thì câu hỏi không có ý nghĩa gì cả. Bạn không thể biến đổi một lớp thành ma thuật khác. Danh mục cũng không hoạt động. – Henry

+0

Tôi nghĩ anh ấy chỉ bối rối về việc đúc, dựa trên những gì anh ấy viết, tôi không nghĩ anh ấy đã vượt qua bất cứ điều gì. Nếu anh ta có, anh ta sẽ nhận lại điều đó, không phải là một thể loại thực sự của ClassA. Tuy nhiên, anh ta muốn "thêm một số chức năng" vào ClassA, đó chính xác là những loại dành cho. –

5

Nếu bạn chỉ muốn thêm phương thức vào đối tượng hiện có, phân lớp phụ không đúng cách. Bạn có thể thêm phương thức vào các lớp hiện có (và các thể hiện của chúng) bằng cách sử dụng một tính năng ngôn ngữ được gọi là category.

Ví dụ:

//"ClassA+doSomethingCool.h" 
@interface ClassA (doSomethingCool) 
- (ClassA *)doSomethingCool:(int)howCool; 
@end 


//"ClassA+doSomethingCool.m" 
@implementation ClassA (doSomethingCool) 
- (ClassA *)doSomethingCool:(int)howCool 
{ 

} 
@end 
2

Đúc không chuyển đổi từ một loại đối tượng khác. Bạn không thể buộc một số thư viện thay đổi kiểu đối tượng và tạo một loại đối tượng khác trừ khi nó sử dụng một số mẫu nhà máy.

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