วิธีการโยนวัตถุใน Objective-C


123

มีวิธีการโยนวัตถุในวัตถุประสงค์ -c เหมือนกับวิธีการโยนวัตถุใน VB.NET หรือไม่?

ตัวอย่างเช่นฉันกำลังพยายามทำสิ่งต่อไปนี้:

// create the view controller for the selected item
FieldEditViewController *myEditController;
switch (selectedItemTypeID) {
    case 3:
        myEditController = [[SelectionListViewController alloc] init];
        myEditController.list = listOfItems;
        break;
    case 4:
        // set myEditController to a diff view controller
        break;
}

// load the view
[self.navigationController pushViewController:myEditController animated:YES];
[myEditController release]; 

อย่างไรก็ตามฉันได้รับข้อผิดพลาดของคอมไพเลอร์เนื่องจากคุณสมบัติ 'list' มีอยู่ในคลาส SelectionListViewController แต่ไม่ได้อยู่บน FieldEditViewController แม้ว่า SelectionListViewController จะสืบทอดจาก FieldEditViewController

สิ่งนี้สมเหตุสมผล แต่มีวิธีแคสต์ myEditController ไปยัง SelectionListViewController เพื่อให้ฉันสามารถเข้าถึงคุณสมบัติ 'รายการ' ได้หรือไม่

ตัวอย่างเช่นใน VB.NET ฉันจะทำ:

CType(myEditController, SelectionListViewController).list = listOfItems

ขอบคุณสำหรับความช่วยเหลือ!

คำตอบ:


216

โปรดจำไว้ว่า Objective-C เป็นส่วนเหนือของ C ดังนั้นการพิมพ์จะทำงานเหมือนใน C:

myEditController = [[SelectionListViewController alloc] init];
((SelectionListViewController *)myEditController).list = listOfItems;

21
หรือ "จำไว้ว่า Objective-C ทำงานเหมือน Java เพียงอย่าลืมเพิ่มเครื่องหมายดอกจันให้กับตัวแปรที่ชี้ไปที่วัตถุ Obj-C"
Dan Rosenstark

1
คำตอบที่ดี คุณสามารถทำให้ชัดเจนขึ้นเล็กน้อยโดยการแบ่งนักแสดงและงานออกเป็นสองบรรทัด
Guido Anselmi

1
การพิมพ์ใน Objective-C นั้นเหมือนกับ C แบบเก่ามากกว่า Java Java ซ่อนสิ่งนี้ส่วนใหญ่จากผู้ใช้ดังนั้นข้อโต้แย้งที่ว่า C ควรได้รับการสอนมากกว่า Java เป็นภาษาแรก
csmith

11
((SelectionListViewController *)myEditController).list

ตัวอย่างเพิ่มเติม:

int i = (int)19.5f; // (precision is lost)
id someObject = [NSMutableArray new]; // you don't need to cast id explicitly

7
โดยทั่วไปสิ่งนี้ถูกต้อง คุณไม่จำเป็นต้องส่ง id ในนิพจน์ข้อความ แต่เมื่อใช้ dot syntax เพื่อเข้าถึงและตั้งค่าคุณสมบัติคุณต้องใช้ชนิดที่เป็นรูปธรรมไม่ใช่แค่ id ดังนั้นคอมไพเลอร์จะรู้ว่าการเรียกใช้เมธอดใดที่จะสร้างจริง (อาจแตกต่างกันสำหรับคุณสมบัติที่มีชื่อเดียวกัน)
Chris Hanson

9

การพิมพ์ใน Objective-C ทำได้ง่ายเช่น:

NSArray *threeViews = @[[UIView new], [UIView new], [UIView new]];
UIView *firstView = (UIView *)threeViews[0];

อย่างไรก็ตามจะเกิดอะไรขึ้นหากวัตถุชิ้นแรกไม่ใช่UIViewและคุณพยายามใช้มัน:

NSArray *threeViews = @[[NSNumber new], [UIView new], [UIView new]];
UIView *firstView = (UIView *)threeViews[0];
CGRect firstViewFrame = firstView.frame; // CRASH!

มันจะผิดพลาด และง่ายต่อการค้นหาข้อขัดข้องสำหรับกรณีนี้ แต่จะเกิดอะไรขึ้นถ้าบรรทัดเหล่านั้นอยู่ในคลาสที่แตกต่างกันและบรรทัดที่สามถูกดำเนินการเพียงครั้งเดียวใน 100 กรณี ฉันพนันได้เลยว่าลูกค้าของคุณพบความผิดพลาดนี้ไม่ใช่คุณ! วิธีแก้ปัญหาที่เป็นไปได้คือการล้มเหลวในช่วงต้นเช่นนี้:

UIView *firstView = (UIView *)threeViews[0];
NSAssert([firstView isKindOfClass:[UIView class]], @"firstView is not UIView");

คำยืนยันเหล่านั้นดูไม่ค่อยดีนักดังนั้นเราจึงสามารถปรับปรุงได้ด้วยหมวดหมู่ที่มีประโยชน์นี้:

@interface NSObject (TypecastWithAssertion)
+ (instancetype)typecastWithAssertion:(id)object;
@end


@implementation NSObject (TypecastWithAssertion)

+ (instancetype)typecastWithAssertion:(id)object {
    if (object != nil)
        NSAssert([object isKindOfClass:[self class]], @"Object %@ is not kind of class %@", object, NSStringFromClass([self class]));
    return object;
}

@end

สิ่งนี้ดีกว่ามาก :

UIView *firstView = [UIView typecastWithAssertion:[threeViews[0]];

ป.ล. สำหรับคอลเลกชันประเภทความปลอดภัย Xcode 7 มีดีกว่าการพิมพ์ตัวอักษรทั่วไปมาก


4

แน่นอนว่าไวยากรณ์เหมือนกับ C - NewObj* pNew = (NewObj*)oldObj;

ในสถานการณ์นี้คุณอาจต้องการพิจารณาให้รายการนี้เป็นพารามิเตอร์ให้กับตัวสร้างสิ่งที่ต้องการ:

// SelectionListViewController
-(id) initWith:(SomeListClass*)anItemList
{
  self = [super init];

  if ( self ) {
    [self setList: anItemList];
  }

  return self;
}

จากนั้นใช้ดังนี้:

myEditController = [[SelectionListViewController alloc] initWith: listOfItems];

0

การแคสต์เพื่อรวมมีความสำคัญพอ ๆ กับการแคสต์เพื่อยกเว้นสำหรับโปรแกรมเมอร์ C ++ การหล่อแบบไม่เหมือนกับ RTTI ในแง่ที่ว่าคุณสามารถโยนวัตถุไปยังประเภทใดก็ได้และตัวชี้ผลลัพธ์จะไม่เป็นศูนย์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.