2012-04-04 32 views
25

Vì vậy, tôi đã chán ngày hôm nay, và quyết định gây rối với C++/Obj-C nội suy, và tôi tìm thấy một cách để tạo ra một thiết lập rất thú vị.Mục tiêu-C - Nhược điểm để kết nối với C++?

@protocol NSCPPObj <NSObject> 

-(id) init; 
-(id) initWithInt:(int) value; 
-(int) somethingThatReturnsAValue; 
-(void) doSomething; 

@end 

class NSCPPObj : objc_object { 
public:  
    static Class cls(); 

    int iVar; 

    NSCPPObj(); 
    NSCPPObj(int); 

    int somethingThatReturnsAValue(); 
    void doSomething(); 
}; 

Như bạn có thể thấy, giao diện khá đơn giản và dễ hiểu. Chúng ta tạo ra hai (gần như) các giao diện giống nhau, một cho một đối tượng C++ và một cho giao thức Obj-C.

Bây giờ, tôi tìm thấy một cách để thực hiện điều này, nhưng cú đúp cho mình, điều này trở nên xấu xí:

// NSCPPObj.mm 
#import <objc/runtime.h> 
#import <iostream> 

#import "NSCPPObject.h" 

Class NSCPPObj_class = nil; 

__attribute__((constructor)) 
static void initialize() 
{ 
    NSCPPObj_class = objc_allocateClassPair([NSObject class], "NSCPPObj", 0); 

    class_addMethod(NSCPPObj_class->isa, @selector(alloc), imp_implementationWithBlock(^(id self) { 
     return class_createInstance(NSCPPObj_class, sizeof(struct NSCPPObj)); 
    }), "@@:"); 

    class_addMethod(NSCPPObj_class, @selector(init), imp_implementationWithBlock(^(id self) { 
     return self;   
    }), "@@:"); 

    class_addMethod(NSCPPObj_class, @selector(initWithInt:), imp_implementationWithBlock(^(id self, int value) { 
     ((struct NSCPPObj *) self)->iVar = value; 

     return self; 
    }), "@@:i"); 

    class_addMethod(NSCPPObj_class, @selector(doSomething), imp_implementationWithBlock(^(id self) { 
     ((struct NSCPPObj *) self)->doSomething(); 
    }), "[email protected]:"); 
    class_addMethod(NSCPPObj_class, @selector(somethingThatReturnsAValue), imp_implementationWithBlock(^(id self) { 
     return ((struct NSCPPObj *) self)->somethingThatReturnsAValue(); 
    }), "[email protected]:"); 

    objc_registerClassPair(NSCPPObj_class); 
} 

Class NSCPPObj::cls() 
{ 
    return NSCPPObj_class; 
} 

NSCPPObj::NSCPPObj() 
{ 
    this->isa = NSCPPObj_class; 
    [((id<NSCPPObj>) this) init]; 
} 

NSCPPObj::NSCPPObj(int value) 
{ 
    this->isa = NSCPPObj_class; 
    [((id<NSCPPObj>) this) initWithInt:value]; 
} 

void NSCPPObj::doSomething() 
{ 
    std::cout << "Value Is: " << [((id<NSCPPObj>) this) somethingThatReturnsAValue] << std::endl; 
} 

int NSCPPObj::somethingThatReturnsAValue() 
{ 
    return iVar; 
} 

tôi sẽ tóm tắt những gì điều này:

  1. Phân bổ một lớp Pair
  2. Thêm tất cả các phương thức lớp và ví dụ vào đối tượng
  3. Đăng ký lớp học Pair

Bây giờ, như bạn có thể thấy, đây không phải là rất linh hoạt, nhưng nó làm việc, và đó là một con đường hai chiều:

id<NSCPPObj> obj = [[NSCPPObj::cls() alloc] initWithInt:15]; 
[obj doSomething]; 

NSLog(@"%i", [obj somethingThatReturnsAValue]); 
NSLog(@"%@", obj); 

NSCPPObj *objAsCPP = (__bridge NSCPPObj *) obj; 

objAsCPP->doSomething(); 
std::cout << objAsCPP->somethingThatReturnsAValue() << std::endl; 

Bạn cũng có thể tạo ra các đối tượng bằng cách sử dụng new NSCPPObj(15), nhưng hãy nhớ xóa đi! Rõ ràng, điều này có thể hoạt động trong môi trường ARC hoặc không phải ARC, nhưng ARC yêu cầu thêm một vài phôi cầu nối.

Vì vậy, tôi đến với câu hỏi thực tế:
Ưu điểm/nhược điểm của cấu trúc thiết kế này là gì? Tôi có thể liệt kê một vài tắt của đỉnh đầu của tôi:

Ưu điểm:

  1. điều hành quá tải với
  2. C++
  3. phương pháp động ràng buộc với ObjC
  4. có thể được xây dựng trong hoặc C++ hay thời trang ObjC

Nhược điểm:

  1. Hard-to-đọc thực hiện
  2. Selectors & bindings phải được bổ sung cho mỗi C++ thực hiện bổ sung vào giao diện
  3. đối tượng Class không thể được tham chiếu trực tiếp

Vì vậy, sau khi tất cả những gì, bạn sẽ khuyên này cấu trúc thiết kế trong một ứng dụng? và tại sao.

+1

Tôi biết quá ít C++ để cung cấp cho câu trả lời này tốt, nhưng tôi tự hỏi nếu câu trả lời cho điều này phụ thuộc vào chính xác loại ứng dụng bạn đang làm việc. Một trò chơi C++ hiện có đang được chuyển sang có thể tìm thấy điều này hữu ích hơn nhiều so với một ứng dụng tiện ích đơn giản ... và một lập trình viên C++ dày dạn có thể đánh giá cao hơn một người có định hướng Mục tiêu-C nhiều. – lxt

+1

Bỏ phiếu để mở lại. Tôi hiểu làm thế nào điều này có thể là 'không xây dựng', nhưng đây là một trang web cộng đồng. Tôi nhận ra bạn là một người kiểm duyệt, nhưng nhìn thấy bạn là người duy nhất muốn điều này đóng lại. –

+0

Tôi không phải là người kiểm duyệt và tôi đã gắn cờ câu hỏi này để xem xét. Nó là chủ đề. – mydogisbox

Trả lời

23

Vì vậy, sau tất cả, bạn có đề xuất cấu trúc thiết kế này trong ứng dụng không? và tại sao.

No.

Đó là một mã thực sự tốt đẹp; Tôi đặc biệt thích sử dụng imp_implementationWithBlock() (nhưng tôi thừa nhận rằng tôi có thể là một phần của tính năng cụ thể của thời gian chạy); Và, tất nhiên, những khám phá như thế này luôn là một công cụ học tập vô cùng quý giá.

Vấn đề, trong bối cảnh "sử dụng dự án trả tiền trong thế giới thực" là bạn đang tạo một cầu tương đối chung, sau đó sẽ có cầu cụ thể ở hai đầu để giao tiếp với thư viện C++ điển hình hoặc Mục tiêu tiêu biểu -C APIs/thư viện. Nói cách khác, bạn đã tạo hiệu quả một thời gian chạy mới bắt nguồn từ sự pha trộn của hai thời gian chạy hiện tại.

Và, như bạn chỉ ra trong Nhược điểm, bạn khá nhiều phải chạm, quấn, sửa đổi và/hoặc gỡ lỗi một shim trên đầu mỗi lớp C++ bạn muốn đưa vào mẫu này.

Khi làm việc với khá nhiều mã Objective-C++ trong hơn 20 năm qua, một cây cầu như thế này thường gặp nhiều rắc rối hơn là giá trị. Bạn có thể tốt hơn - dành ít thời gian viết và gỡ lỗi mã - tạo các trình bao bọc Objective-C đơn giản xung quanh các API C++ (hoặc C, thẳng thắn) mà sau đó có thể được tích hợp và tiêu thụ bởi các khung Objective-C của hệ thống được nhắm mục tiêu.

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