2011-12-14 27 views
8

tôi đã cố gắng để trả lời Using a UITableView subclass with a UITableViewController với ISA Switching như vậy:Mục tiêu-C: Làm thế nào để thay đổi lớp của một đối tượng trong thời gian chạy?

self.tableView->isa = [MyTableView class]; 

Nhưng, tôi nhận được lỗi biên dịch: Instance variable 'isa' is protected.

Có cách nào để làm được việc này? Và, nếu có, có an toàn không?

Tôi hỏi vì @AmberStar's answer to that question có vẻ hơi thiếu sót. (Xem bình luận của tôi.)

Trả lời

22

Nếu lớp học của bạn xem bảng cung cấp BẤT K storage bộ nhớ nào sẽ bị hỏng. Tôi sẽ không đề nghị con đường bạn đi xuống. Nhưng phương pháp chính xác sẽ là sử dụng object_setClass(tableView, [MyTableView class]).

Hãy đảm bảo đây thực sự là điều bạn muốn.

Dưới đây là một mẫu mã nhỏ cho thấy đây là một ý tưởng khủng khiếp.

#import <objc/runtime.h> 

@interface BaseClass : NSObject 
{ 
    int a; 
    int b; 
} 
@end 

@implementation BaseClass 

@end 

@interface PlainSubclass : BaseClass 
@end 

@implementation PlainSubclass 
@end 

@interface StorageSubclass : BaseClass 
{ 
@public 
    int c; 
} 
@end 

@implementation StorageSubclass 
@end 



int main(int argc, char *argv[]) 
{ 
    BaseClass *base = [[BaseClass alloc] init]; 
    int * random = (int*)malloc(sizeof(int)); 
    NSLog(@"%@", base); 

    object_setClass(base, [PlainSubclass class]); 
    NSLog(@"%@", base); 

    object_setClass(base, [StorageSubclass class]); 
    NSLog(@"%@", base); 
    StorageSubclass *storage = (id)base; 
    storage->c = 0xDEADBEEF; 
    NSLog(@"%X == %X", storage->c, *random); 
} 

và đầu ra

2011-12-14 16:52:54.886 Test[55081:707] <BaseClass: 0x100114140> 
2011-12-14 16:52:54.889 Test[55081:707] <PlainSubclass: 0x100114140> 
2011-12-14 16:52:54.890 Test[55081:707] <StorageSubclass: 0x100114140> 
2011-12-14 16:52:54.890 Test[55081:707] DEADBEEF == DEADBEEF 

Như bạn có thể thấy ghi vào storage->c viết bên ngoài bộ nhớ phân bổ cho các trường hợp, và vào khối Tôi phân bổ cho ngẫu nhiên. Nếu đó là một đối tượng khác, bạn chỉ cần phá hủy con trỏ isa của nó.

+0

Cảm ơn bạn. Tuy nhiên, có an toàn không khi đặt 'self.tableView' trong' UITableViewController' thành một thể hiện mới của một lớp con tùy chỉnh của 'UITableView'? – ma11hew28

+1

nói @JoshuaWeinberg, bạn có biết rằng sẽ rất tệ nếu sử dụng [Tham chiếu liên kết] (http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html) Tôi biết nó an toàn để sử dụng với các loại (sử dụng nó bản thân mình nhiều lần) nhưng nếu phân lớp cần một biến, tôi nghĩ rằng điều này sẽ làm các trick ... –

+1

Tôi sử dụng chúng tất cả các thời gian, khá tiện dụng. –

5

Cách an toàn là tạo một phiên bản mới.

Hoán đổi isa không an toàn - bạn không biết bố cục bộ nhớ của lớp là gì hoặc nó sẽ là gì trong tương lai. Ngay cả việc di chuyển đồ thị thừa kế cũng không thực sự an toàn vì việc khởi tạo và hủy diệt đối tượng sẽ không được thực hiện đúng cách - để đối tượng của bạn ở trạng thái không hợp lệ (có thể làm cho toàn bộ chương trình của bạn bị hỏng).

+0

Cool, cảm ơn. Tuy nhiên, có an toàn không khi đặt 'self.tableView' trong' UITableViewController' thành một thể hiện mới của một lớp con tùy chỉnh của 'UITableView'? – ma11hew28

+2

@Matt có, bạn có thể phân lớp 'UITableView', nhưng bạn phải hủy bỏ việc xây dựng lại bảng trong quá trình thay đổi loại bảng. trong các tình huống phức tạp hơn, bạn có thể ưu tiên một đối tượng có thể được hoán đổi dễ dàng hơn (ví dụ: một đối tượng theo kiểu dáng hoặc trình bày dữ liệu theo cách khác). – justin

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