2012-05-26 36 views
7

Nói rằng tôi có lớpTạo lớp con với tài sản lớp con khác nhau

@interface CustomClass : NSObject 

@property (nonatomic, strong) NSArray * nicestArrayEver; 

@end 

này Và tôi muốn tạo một lớp con của CustomClass, nhưng ở đây là bắt

@interface ASubClassCustomClass : CustomClass 

@property (nonatomic, strong) NSMutableArray * nicestArrayEver; 

@end 

Vấn đề là bạn có thể tưởng tượng là khi tôi khởi tạo ASubClassCustomClass và gọi nó là super initializer (vì có các thuộc tính khác được yêu cầu) nicestArrayEver không thể sửa đổi được tạo .. làm thế nào tôi có thể tránh tạo ra nó để tôi có thể thiết lập một cái có thể thay đổi được?

Lưu ý: Đây chỉ là một ví dụ, việc triển khai thực sự gọi một lớp con nặng để tạo và thực sự tùy chỉnh (không phải là NSArray).

Trả lời

5

Bạn có thể làm cho nó hoạt động, bằng cách sử dụng các biến sự ủng hộ khác nhau, khi tổng hợp nó trông như thế này: @synthesize nicestArrayEver = nicestArrayEverSubClass_;

#import <Foundation/Foundation.h> 

@interface CustomClass : NSObject 

@property (nonatomic, strong) NSArray * nicestArrayEver; 

@end 

@implementation CustomClass 
@synthesize nicestArrayEver ; 

-(id)init 
{ 
    if (self = [super init]) { 
     nicestArrayEver = [[NSArray alloc] init]; 
    } 
    return self; 
} 
@end 

@interface ASubClassCustomClass : CustomClass 

@property (nonatomic, strong) NSMutableArray * nicestArrayEver; 

@end 

@implementation ASubClassCustomClass 
@synthesize nicestArrayEver = nicestArrayEverSubClass_; 

-(id)init{ 
    if (self = [super init]) { 
     nicestArrayEverSubClass_ = [[NSMutableArray alloc] init]; 
    } 
    return self; 
} 
@end 



int main(int argc, const char * argv[]) 
{ 

    @autoreleasepool { 

     CustomClass *c1 = [[[CustomClass alloc] init] autorelease]; 
     ASubClassCustomClass *c2 = [[[ASubClassCustomClass alloc] init] autorelease]; 

     NSLog(@"%@", NSStringFromClass([[c1 nicestArrayEver] class])); 
     NSLog(@"%@", NSStringFromClass([[c2 nicestArrayEver] class])); 

    } 
    return 0; 
} 

đầu ra

2012-05-27 01:59:16.221 NicestArray[2312:403] __NSArrayI 
2012-05-27 01:59:16.225 NicestArray[2312:403] __NSArrayM 

cách tiếp cận khác có thể là để có 2 phương thức init trong lớp cơ sở, một phương thức sẽ khởi tạo thuộc tính và một, điều đó sẽ không, nhưng để lại nhiệm vụ đó cho con c lass - điều này sẽ ngăn cản bạn tạo ra các vật đắt tiền chỉ để vứt chúng đi.
Bây giờ lớp cơ sở có thể được khởi tạo trực tiếp với init thứ hai và đi đến trạng thái sai. Bạn có thể tránh điều này bằng cách kiểm tra loại tự lớp với isMemberOfClass: và ném một lỗi, nếu loại lớp là lớp cơ sở.

@interface CustomClass : NSObject 

@property (nonatomic, strong) NSArray * nicestArrayEver; 
-(id)initWithoutArray; 
@end 

@implementation CustomClass 
@synthesize nicestArrayEver ; 

-(id) initWithoutArray 
{ 
    if (self = [super init]) { 
     if ([self isMemberOfClass:[CustomClass class]]) { 
      [NSException raise:@"AbstractMethodCall" format:@"%@ should be called only from Subclasses of %@", NSStringFromSelector(_cmd), NSStringFromClass([self class])]; 
     } 
    } 
    return self; 
} 


-(id)init 
{ 
    if (self = [super init]) { 
     nicestArrayEver = [[NSArray alloc] init]; 
    } 
    return self; 
} 
@end 

@interface ASubClassCustomClass : CustomClass 

@property (nonatomic, strong) NSMutableArray * nicestArrayEver; 

@end 

@implementation ASubClassCustomClass 
@synthesize nicestArrayEver = nicestArrayEverSubClass_; 

-(id)init{ 
    if (self = [super initWithoutArray]) { 
     nicestArrayEverSubClass_ = [[NSMutableArray alloc] init]; 
    } 
    return self; 
} 

@end 



int main(int argc, const char * argv[]) 
{ 

    @autoreleasepool { 

     CustomClass *c1 = [[[CustomClass alloc] init] autorelease]; 
     ASubClassCustomClass *c2 = [[[ASubClassCustomClass alloc] init] autorelease]; 

     NSLog(@"%@", NSStringFromClass([[c1 nicestArrayEver] class])); 
     NSLog(@"%@", NSStringFromClass([[c2 nicestArrayEver] class])); 

     //this works, as it is the subclass 
     ASubClassCustomClass *shouldWork = [[[ASubClassCustomClass alloc] init] autorelease]; 

     // ouch! 
     CustomClass *shouldCrash = [[[CustomClass alloc] initWithoutArray] autorelease]; 

    } 
    return 0; 
} 
+1

+1 để trả lời câu hỏi và để chứng minh cho tôi sai. :) –

-1

Bạn thực sự không thể làm điều đó. Chỉ cần tạo CustomClass với NSMutableArray. Bạn có thể tạo chúng dưới dạng id và kiểm tra isKindOfClass: nhưng đó chỉ là việc vặt và không thực sự cần thiết.

Có thực sự chỉ có hai lý do tôi có thể thấy để làm những gì bạn đang yêu cầu:

  1. Để tránh những phí thêm NSMutableArray
  2. Để ngăn chặn CustomClass từ sửa đổi các nội dung của mảng trừ khi đó là a ASubClassCustomClass.

Trong khi đây là những mục tiêu tốt, tôi muốn nói rằng trong trường hợp này, bạn nên đơn giản hóa một chút.

+0

Josh, vui lòng xem Lưu ý tôi vừa thêm. Tôi đồng ý với bạn, nếu đó là NSArray, nhưng trong trường hợp này, không phải là và nặng. –

+0

Dù bằng cách nào, bạn vẫn ở trên cùng một chiếc thuyền. Có thể đáng xem xét lại thiết kế của bạn. Nếu một đứa trẻ có một loại thông tin khác với siêu lớp của nó, nó có thể đáng lưu trữ trong một biến khác. Nếu bạn muốn bạn có thể tạo một phương thức lớp trả về biến thích hợp tùy thuộc và ghi đè lên đó trong lớp con của bạn. –

+0

Trên thực tế, hoàn toàn có thể có một thuộc tính có cùng tên nhưng một loại khác trong một phân lớp. Một trong những bắt là (trừ khi khả năng hiển thị của ivar của siêu lớp đã được thay đổi) ivar ủng hộ phải có một tên khác nhau. Xem câu trả lời của http://stackoverflow.com/a/10690692/ hoặc vikingosegundo cho câu hỏi này. –

1

Tôi không thấy lý do tại sao bạn sẽ muốn làm điều đó, nhưng tôi sẽ khuyên bạn nên làm như sau: trong lớp con bạn khai báo một tài sản NSMutableArray riêng biệt (chúng ta hãy gọi nó nicestMutableArrayEver) và ghi đè lên các getter cho thuộc tính superclass NSArray của bạn để trả lại cá thể mutableArray:

- (NSArray *)nicestArrayEver { 
    return [self nicestMutableArrayEver]; 
} 

Bằng cách này, bạn có thể lấy lại mutableArray bất cứ khi nào bạn tham chiếu thuộc tính lớp cha.

Tốt nhất,

+0

Tôi không thấy, tại sao ai đó không muốn làm điều đó. Mối quan hệ này giữa CustomClass và ASubClassCustomClass là một chuyên môn từ một trường hợp chung cho một trường hợp đặc biệt hơn - một mẫu rất điển hình trong thiết kế định hướng đối tượng. Cũng lưu ý rằng, với cách tiếp cận của bạn, phôi sẽ cần thiết liên tục quá tắt cảnh báo trình biên dịch. – vikingosegundo

1

Thuộc tính hầu như không bao giờ có loại có thể thay đổi.Nếu họ làm, sau đó một người gọi có thể có được con trỏ và biến đổi các thuộc tính của đối tượng đằng sau lưng của nó. Nếu một tài sản nên được bên ngoài có thể thay đổi, đó phải là thông qua các phương pháp đột biến.

Hãy nhớ rằng các thuộc tính xác định giao diện chứ không phải triển khai. @synthesize có thể tạo triển khai từ khai báo thuộc tính, nhưng nếu điều đó sẽ làm điều sai, bạn không nên sử dụng nó.

Vì vậy, bước đầu tiên là xác định giao diện cho lớp và lớp con của bạn, mà không tính đến việc triển khai. Chỉ sau khi bạn biết những gì các giao diện nên được bạn nên thiết kế việc thực hiện của mỗi.

Cho dù thuộc tính có thể biến đổi bên ngoài hay không, biến thể hiện sao lưu có thể cần phải thay đổi. Lớp học có thể cần phải thay đổi hoàn toàn thuộc tính của riêng nó.

Bạn có thể làm cho lớp cơ sở có trình khởi tạo được chỉ định lấy đối tượng mảng làm tham số (loại id để ghi đè của lớp con không phải truyền để coi nó là NSMutableArray*). Sau đó, bộ khởi tạo "bình thường" của lớp sẽ gọi trình khởi tạo được chỉ định đó với NSArray để sử dụng. Trình khởi tạo được chỉ định của lớp con sẽ gọi trình khởi tạo được chỉ định của lớp cha, đi qua trong số NSMutableArray để sử dụng.

Hoặc, bộ khởi tạo lớp cơ sở có thể gọi ra phương thức khác để lấy mảng. Việc triển khai lớp cơ sở sẽ trả lại một NSArray. Lớp con có thể ghi đè phương thức đó để trả về một số NSMutableArray.

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