2009-08-21 31 views

Trả lời

17

Tất cả đều liên quan đến việc gọi điện ABI.

Xem xét các phương pháp:

- (id)initWithFormat:(NSString *)format, ...; 
+ (id)arrayWithObjects:(id)firstObj, ...; 
+ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...; 

Các ... nói với trình biên dịch rằng một số biến của tham số của bất kỳ loại có thể có mặt. Không có cách nào cho trình biên dịch biết những gì các loại được yêu cầu phải được (các định nghĩa thực sự có dấu hiệu giúp với điều đó).

Bây giờ, hãy xem xét ba phương pháp. Cả ba đều có các yêu cầu rất khác nhau về những gì có thể có trong danh sách đối số biến. Một mảng phải là một loạt các đối tượng theo sau là một số không. Từ điển yêu cầu một loạt các đối tượng theo sau là một số không. Cuối cùng, phương thức chuỗi yêu cầu một loạt các đối số khớp với các kiểu trong chuỗi định dạng.

Tất cả các hành vi này được gắn trực tiếp với phương thức được gọi và, nếu tác giả của API quyết định "khó sử dụng", hành vi giải mã các đối số biến có thể được sửa đổi trong thời gian chạy, chỉ để thực hiện cuộc sống khó khăn.

Dòng dưới cùng: C ABI không có cú pháp cho phép xác định rằng một phương thức hoặc hàm nhận một số biến đối số với bất kỳ loại ràng buộc nào trên đối số hoặc kết thúc của chúng.

Mục tiêu-C có thể thay đổi các quy tắc chỉ để khai báo phương thức & yêu cầu, nhưng điều đó sẽ không hỗ trợ chức năng C hoặc C++, cả hai mục tiêu-C phải vẫn tương thích.

+0

Dường như trình biên dịch có thể minh bạch thêm một nil cuối cùng vào [mảng NSArrayWithObjects: a, b, c]. Điều đó sẽ để lại một gotcha rằng nil sẽ không được phép như một đối số. Chúng tôi thậm chí có thể thực hiện việc này ngay bây giờ. Những người đã bao gồm các nil sẽ chỉ nhận được [NSArray arrayWithObjects: a, b, c, nil, nil], mà tôi nghĩ là không gây tử vong. – Ken

+2

Ken, đưa các trường hợp đặc biệt vào trình biên dịch cho các lớp đặc biệt như NSArray mở ra một lượng lớn các sâu. Điều gì sẽ xảy ra nếu bạn phân lớp NSArray? – NSResponder

+0

@Ken: Tuy nhiên, bạn không thể thêm 'nil' vào' NSArray' (bạn phải sử dụng 'NSNull' thay thế). – mipadi

3

Bất kỳ hàm varargs nào cũng đòi hỏi một cách nào đó để biết có bao nhiêu tham số - nếu bạn không kết thúc, bạn chỉ cần cái gì khác. Trong trường hợp này, sự thay thế rõ ràng là độ dài, nhưng sau đó bạn cần phải cập nhật đối số chiều dài mỗi lần bạn thay đổi số lượng mục trong mảng và điều đó có thể rườm rà hoặc bị hỏng.

Tôi đoán bạn có một mảng có thể chứa nil?

+1

NSArray không thể chứa nil - bạn phải sử dụng NSNull để đại diện cho nó. Tôi đoán anh ta chỉ bị cắn bởi các lỗi gây phiền nhiễu có thể là kết quả của việc quên đi terminator nil. – Chuck

+0

Nó không phải là nhiều bằng cách quên mất terminator nil, nó chỉ là tôi đang sử dụng để các ngôn ngữ khác như Java và Ruby cho phép varargs mà không có một terminator nil. Có lẽ những gì tôi hỏi là tại sao Obj-C yêu cầu một terminator nil cho varargs? Tại sao trình biên dịch không thể "làm đúng" cho [mảng NSWrayWithObjects: @ "foo", @ "bar"]? Câu hỏi của tôi là nhiều hơn một câu hỏi về sự tò mò của lý do tại sao ngôn ngữ yêu cầu này. Đó là một mụn cóc rõ ràng khi đến từ các ngôn ngữ khác. Đó là bởi vì đó là cách varargs trong các hàm C? – pjb3

+3

Tôi _believe_ varargs mục tiêu-c được xây dựng trên các var var tiêu chuẩn C không vượt qua số lượng đối số. Có thể hiểu được obj-c có thể đã làm điều đó trở lại trong việc thực hiện ban đầu của nó, nhưng bây giờ nó bị ràng buộc bởi khả năng tương thích ABI. – olliej

2

@bbum cung cấp một số chi tiết kỹ thuật tuyệt vời. Dưới đây là một số suy nghĩ về thực tế "tại sao họ không sửa chữa nó?"

Hãy nhớ rằng: C chỉ là lắp ráp và Obj-C chỉ là C ... Nó có thể được thiết kế lại tất nhiên, nhưng hầu như không có áp lực để làm như vậy. Bạn vẫn không thể đặt nil trong một mảng (điều đó sẽ đòi hỏi một sự thay đổi lớn). Trình biên dịch bây giờ cảnh báo bạn nếu bạn quên nil, do đó, nó không phải là một cái gì đó các nhà phát triển phàn nàn rất nhiều về. Thương mại-off là giữ ngôn ngữ nhiều (nhiều!) Đơn giản hơn so với thân nhân của nó, nhận được những lợi ích của nhiều thập kỷ tối ưu hóa C-compiler và đảm bảo khả năng tương thích với mã C.

Một điều nên trở nên rõ ràng từ thảo luận của bbum @: NSArray không phải là tính năng ngôn ngữ của Mục tiêu-C. C-mảng là một tính năng ngôn ngữ, nhưng NSArrays chỉ là một đối tượng khác, không khác với các đối tượng bạn viết.

2

Có nhiều cách giải quyết khác nhau, nhưng yêu thích hiện tại của tôi là tốt cho các ứng dụng nhiều hơn cho các khung công bố. Chấp nhận một NSArray trong phương thức của bạn thay vì "...", và sau đó điền nó với macro tiện lợi bên dưới, được đặt trong tệp tiền tố của bạn hoặc tiêu đề tiện ích.

#define $ array (objs ...) [NSArray arrayWithObjects: objs, nil]

Nó sẽ cho phép nhiều đối số có độ dài thay đổi được gắn nhãn và bạn sẽ tự giải thoát khỏi mô hình cổ xưa của việc sử dụng đối số đầu tiên và sau đó va_list và anh em của nó có lợi một vòng lặp for-in hoặc nhiều công cụ thu thập khác có sẵn.

[self findMatchingSetAndAlert: @ "title" ties: $ array (tie1, tie2, tie3) shirts: $ array (shirt1, shirt2, shirt3, shirt4)];

Nếu ai đó biết cách triển khai danh sách không được phân tách bằng không, chẳng hạn như stringWithFormat, vui lòng cho chúng tôi biết! Nó sử dụng các thuộc tính và macro hoặc một cái gì đó được thiết kế đặc biệt để định dạng, nhưng chúng được thực hiện bằng cách nào đó.

+0

Câu trả lời của tôi khá cũ và các macro không còn cần thiết như một cái nạng nữa. Cú pháp C Mục tiêu hiện đại giúp ích rất nhiều, vì bây giờ bạn có thể tạo một mảng như '@ [@" firstString ", @" secondString "]'. Bây giờ chúng ta có thể lo lắng về các loại thay vì cú pháp. –

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