2009-06-17 19 views
9

Tôi có nhiều năm kinh nghiệm trong Obj-c và Cocoa, nhưng bây giờ tôi mới trở lại và Obj-C 2.0, v.v. đầu của tôi xung quanh thời gian chạy hiện đại và tuyên bố tài sản, vv Một điều mà confuses tôi một chút là khả năng trong thời gian chạy hiện đại để có iVars tạo ra ngầm. Và tất nhiên điều này ngụ ý rằng trong mã của bạn, bạn nên luôn sử dụng self.property để truy cập vào giá trị.Sử dụng các biến mẫu với Thời gian chạy hiện đại

Tuy nhiên, trong các phương thức init * và dealloc (giả sử bạn không sử dụng GC), chúng tôi nên sử dụng trực tiếp iVar (trong thời gian chạy hiện tại).

Vì vậy, câu hỏi là:

  1. Chúng ta có nên sử dụng accessors tài sản trong init * và dealloc với Modern Runtime?

  2. Nếu vậy, tại sao điều này lại khác? Có phải chỉ vì trình biên dịch không thể nhìn thấy iVar?

  3. Nếu tôi cần ghi đè người truy cập, tôi vẫn có thể truy cập iVar đó sẽ được xác định khi chạy hoặc tôi có phải xác định iVar thực tế mà thời gian chạy sau đó sẽ sử dụng không?

  4. Một lần nữa, nếu tôi có thể truy cập vào được tổng hợp Ivar, tại sao tôi không thể tiếp tục làm việc này cho các * init và phương pháp dealloc?

Tôi đọc tài liệu nhiều lần, nhưng dường như họ mơ hồ về tất cả điều này và tôi muốn chắc chắn rằng tôi hiểu rõ điều này để quyết định cách tôi muốn tiếp tục mã hóa.

Hy vọng rằng câu hỏi của tôi rõ ràng.


Nhanh chóng tóm tắt của thử nghiệm:

  1. Nếu bạn không khai báo Ivar trong di sản, trình biên dịch là hoàn toàn không hài lòng

  2. Nếu bạn sử dụng #ifndef __OBJC2__ xung quanh Ivar trong trình biên dịch di sản vui vẻ và bạn có thể sử dụng cả trực tiếp ivar và làm thuộc tính

  3. Trong runtime hiện đại, bạn có thể rời khỏi Ivar undefined và truy xuất thuộc tính

  4. Trong thời gian chạy hiện đại, cố gắng truy cập Ivar trực tiếp mà không khai báo cho lỗi trong quá trình biên dịch khai

  5. @private của Ivar, tất nhiên, cho phép truy cập trực tiếp ivar, trong cả di sản và hiện đại

Không thực sự đưa ra một cách sạch sẽ để tiến lên ngay bây giờ phải không?

+3

Để làm rõ những người có thể nhầm lẫn, "Thời gian chạy hiện đại" đề cập đến thời gian chạy Objective-C mới có thể * * được sử dụng bởi các ứng dụng iPhone và các quy trình 64 bit trên 10.5 trở lên. Bất kỳ mã Objective-C nào chạy trong 32 bit hoặc trên Tiger hoặc trước đó đều chạy trong "Thời gian chạy cũ". –

+0

http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html –

+0

@Quinn - Tôi không biết rằng hệ điều hành iPhone sử dụng thời gian chạy hiện đại. Điều đó khá ngọt ngào. –

Trả lời

10

Trong trình biên dịch hiện tại (OS X 10.5/GCC 4.0.1), bạn không thể truy cập trực tiếp vào các bộ tổng hợp thời gian chạy được tổng hợp. Greg Parker, một trong những kỹ sư chạy hệ điều hành OS X đặt nó theo cách this trên danh sách cacao-dev (ngày 12 tháng 3 năm 2009):

Bạn không thể trong trình biên dịch hiện tại. A trình biên dịch trong tương lai nên khắc phục điều đó. Sử dụng @ ivient riêng rõ ràng trong thời gian chờ đợi. Một @private ivar không được được coi là một phần của hợp đồng - đó là ý nghĩa của @private, được thực thi bởi cảnh báo trình biên dịch và liên kết lỗi.

Và tại sao không có cách nào để khai báo rõ ràng các biến mẫu trong tệp .m cho thời gian chạy mới?

Ba lý do: (1) có một số chi tiết thiết kế không tầm thường để làm việc ra, (2) biên dịch-engineer giờ là hạn chế, và (3) ivars @private là đủ nhìn chung là tốt.

Vì vậy, hiện tại bạn phải sử dụng ký hiệu chấm để truy cập các thuộc tính, ngay cả trong initdealloc. Điều này đi ngược lại cách thực hành tốt nhất của việc sử dụng ivars trực tiếp trong những trường hợp này, nhưng không có cách nào xung quanh nó. Tôi thấy rằng sự dễ dàng của việc sử dụng các bảng tổng hợp thời gian chạy tổng hợp (và các lợi ích hiệu suất) lớn hơn trong hầu hết các trường hợp. Nơi bạn cần truy cập trực tiếp vào ngà voi, bạn có thể sử dụng @private ivar như Greg Parker gợi ý (không có gì ngăn cản bạn trộn lẫn các bảng trắng được khai báo rõ ràng và thời gian chạy tổng hợp).

Cập nhật Với OS X 10.6, thời gian chạy 64 bit cho phép truy cập trực tiếp vào tổng hợp ivars qua self->ivar.

+0

Cảm ơn Barry, đây là những gì tôi nghĩ và hy vọng sẽ xác nhận. Như tôi đã đề cập trong một bình luận dưới đây, tôi sẽ cho bài kiểm tra này trước khi thực sự đánh dấu là đã trả lời. –

+0

Được rồi, tôi vừa xác nhận rằng điều này thực sự hoạt động như được mô tả ở đây. Xem chỉnh sửa ở trên để biết kết quả. –

+0

Có vẻ như bạn có thể bỏ qua phần "tự-" và chỉ sử dụng tên ivar trực tiếp. Điều này dường như không làm việc cho thời gian chạy iPhone hiện nay mặc dù. – briankc

0

another SO question có thông tin tương tự nhưng không hoàn toàn trùng lặp.

Điểm mấu chốt, từ Objective-C 2.0 documentation, và trích dẫn từ Mark Bessey's answer là như sau:

Có sự khác biệt trong hành vi mà phụ thuộc vào thời gian chạy (xem thêm “Runtime khác biệt”):

Đối với các thời gian chạy kế thừa, các biến mẫu phải được khai báo trong khối @interface. Nếu một biến cá thể cùng tên và kiểu tương thích với thuộc tính tồn tại, nó được sử dụng — nếu không, bạn sẽ gặp lỗi trình biên dịch.

Đối với các thời gian chạy hiện đại, các biến mẫu được tổng hợp khi cần. Nếu một biến mẫu cùng tên đã tồn tại, nó được sử dụng.

hiểu biết của tôi là như sau:

Bạn không nên sử dụng accessors tài sản trong init*dealloc phương pháp, vì những lý do tương tự mà bạn không nên sử dụng chúng trong thời gian chạy cũ: Nó cho phép bạn mở các lỗi tiềm năng nếu sau này bạn ghi đè các phương thức thuộc tính và kết thúc bằng cách thực hiện một việc không nên thực hiện trong init* hoặc dealloc.

Bạn sẽ có thể để cả hai tổng hợp Ivar ghi đè lên các phương pháp tài sản như sau:

@interface SomeClass 
{ 
} 
@property (assign) int someProperty; 
@end 

@implementation SomeClass 
@synthesize someProperty; // this will synthesize the ivar 
- (int)someProperty { NSLog(@"getter"); return someProperty; } 
- (void)setSomeProperty:(int)newValue 
{ 
    NSLog(@"setter"); 
    someProperty = newValue; 
} 
@end 

Dẫn tôi nghĩ rằng bạn sẽ có thể truy cập Ivar tổng hợp trong init*dealloc của bạn phương pháp là tốt. Chữ ký duy nhất tôi có thể nghĩ là dòng @synthesize có thể phải đến trước khi định nghĩa của các phương thức init*dealloc của bạn trong tệp nguồn.

Cuối cùng, vì có các thanh ngang được khai báo trong giao diện vẫn hoạt động, đó vẫn là đặt cược an toàn nhất của bạn.

+0

Hmm, nhận xét này dường như mâu thuẫn với những gì Barry đề cập đến trong việc bỏ qua Greg Parker và cũng trực giác có vẻ sai (mặc dù trực giác đôi khi có thể là crap :-)). Có vẻ như tuyên bố iVar riêng sẽ là con đường để đi. –

+0

Dựa trên câu trả lời của mình, Barry Wark có nhiều kinh nghiệm trong vấn đề này hơn tôi. Tôi sẽ tin tưởng trực giác của anh ta trên tôi:) –

1

Kể từ biến dụ mình chỉ có thể được tổng hợp trong thời gian chạy hiện đại (và phải được khai báo trong @ interface dưới 32-bit hoặc pre-Leopard), đó là an toàn nhất/nhất cầm tay cũng tuyên bố Ivar

  • Chúng ta có nên sử dụng trình truy cập thuộc tính trong init * và dealloc với Runtime hiện đại không?

Quy tắc của ngón tay cái là "có thể" cho -init*, và "thường không" cho -dealloc.

Khi khởi tạo một đối tượng, bạn muốn đảm bảo sao chép/giữ lại giá trị đúng cho ivars. Trừ khi setter của tài sản có một số tác dụng phụ mà làm cho nó không phù hợp để khởi tạo, chắc chắn tái sử dụng trừu tượng tài sản cung cấp.

Khi deallocating một đối tượng, bạn muốn phát hành bất kỳ đối tượng ivar nào, nhưng không lưu trữ các đối tượng mới. Một cách dễ dàng để làm điều này là đặt thuộc tính thành số không (myObject.myIvar = nil), về cơ bản gọi là [myObject setMyIvar:nil]. Vì các thông điệp đến nil bị bỏ qua, không có nguy hiểm trong việc này. Tuy nhiên, nó quá mức cần thiết khi [phát hành myIvar]; thường là tất cả những gì bạn cần.Nói chung, không sử dụng thuộc tính (hoặc trực tiếp, setter) trong các tình huống mà deallocation sẽ hoạt động khác với thiết lập biến.

Tôi có thể hiểu đối số của eJames về việc sử dụng trình truy cập thuộc tính trong init/dealloc, nhưng flipside là nếu bạn thay đổi hành vi thuộc tính (ví dụ: thay đổi từ giữ lại thành bản sao hoặc chỉ gán mà không giữ lại) và don 't sử dụng nó trong init, hoặc ngược lại, hành vi có thể được ra khỏi đồng bộ quá. Nếu khởi tạo và sửa đổi một ivar nên hành động giống nhau, sử dụng accessor thuộc tính cho cả hai.

  • Nếu vậy, tại sao điều này lại khác? Có phải chỉ vì trình biên dịch không thể nhìn thấy ngà?

Các giao dịch runtime hiện đại với quy mô lớp học và bố trí một cách thông minh hơn, đó là lý do bạn có thể thay đổi cách bố trí của ivars mà không cần phải biên dịch lại các lớp con. Nó cũng có thể suy ra tên và loại ivar bạn muốn từ tên và loại thuộc tính tương ứng. Các Objective-C 2.0 Runtime Programming Guide có thêm thông tin, nhưng một lần nữa, tôi không biết làm thế nào sâu sắc các chi tiết giải thích ở đó.

  • Nếu tôi cần phải ghi đè một accessor, có thể tôi vẫn truy cập mà Ivar đó sẽ được xác định tại thời gian chạy hoặc làm tôi phải xác định một Ivar thực tế mà bộ thực thi sau đó sẽ sử dụng không?

Tôi chưa thử nghiệm điều này, nhưng tôi tin rằng bạn được phép truy cập mã được đặt tên theo mã, vì nó thực sự phải được tạo. Tôi không chắc liệu trình biên dịch có phàn nàn hay không, nhưng tôi đoán rằng vì nó sẽ cho phép bạn tổng hợp ngà voi mà không phàn nàn, nó cũng đủ thông minh để biết về ivar tổng hợp và cho phép bạn tham khảo nó theo tên.

  • Một lần nữa, nếu tôi có thể truy cập vào Ivar tổng hợp, tại sao tôi không thể tiếp tục làm việc này cho các * init và phương pháp dealloc?

Bạn sẽ có thể truy cập thuộc tính và/hoặc ivar bất cứ lúc nào sau khi cá thể được phân bổ.

+0

Thật không may là các tài liệu không đi sâu hơn vào điều này. Đó là lý do cho các câu hỏi ở đây. Bạn đang chỉ về iVars "phải được tạo ra" mặc dù là gây hiểu lầm. Lý do thời gian chạy hiện đại có thể bố trí cấu trúc lớp tốt hơn là bởi vì nó thực hiện nó khi chạy. Do đó, các biến này chưa tồn tại. Tuy nhiên, tôi nghĩ rằng tôi sẽ chạy một số thử nghiệm trong một ứng dụng 64-bit và báo cáo lại. –

0

Tôi đang gặp sự cố tương tự. Con đường tôi đang làm việc xung quanh không thể truy cập vào các biến Ví dụ tổng hợp như sau:

tiêu đề công cộng

@interface MyObject:NSObject { 
} 
@property (retain) id instanceVar; 
@property (retain) id customizedVar; 
@end 

tiêu đề tin/thực hiện

@interface MyObject() 
@property (retain) id storedCustomizedVar; 
@end 

@implementation MyObject 
@synthesize instanceVar, storedCustomizedVar; 
@dynamic customizedVar; 

- customizedVar { 
    if(!self.storedCustomizedVar) { 
    id newCustomizedVar; 
    //... do something 
    self.storedCustomizedVar= newCustomizedVar; 
    } 
    return self.storedCustomizedVar; 
} 

- (void) setCustomizedVar:aVar { 
    self.storedCustomizedVar=aVar; 
} 

@end 

Nó không phải là thanh lịch, nhưng ít ít nhất nó giữ sạch tập tin tiêu đề công khai của tôi.

Nếu bạn sử dụng KVO, bạn cần xác định customVar làm khóa phụ thuộc của savedCustomizedVar.

0

Tôi khá mới đối với Obj-C (nhưng không phải lập trình) và cũng đã bị nhầm lẫn bởi chủ đề này.

Khía cạnh khiến tôi lo ngại là dường như vô tình dễ dàng sử dụng iVar thay vì thuộc tính. Ví dụ: viết:

myProp = someObject;

thay vì

self.myProp = someObject; Phải thừa nhận rằng đây là lỗi "người dùng", nhưng nó vẫn có vẻ khá dễ dàng để làm vô tình trong một số mã, và cho một tài sản giữ lại hoặc nguyên tử có thể dẫn đến các vấn đề.

Lý tưởng tôi muốn có thể có thời gian chạy để áp dụng một số mẫu cho tên thuộc tính khi tạo bất kỳ iVar nào. Ví dụ. luôn luôn tiền tố chúng với "_".

Trong thực tế tại thời điểm này tôi đang làm điều này một cách thủ công - khai báo rõ ràng ivars của tôi, và cố ý cho họ tên khác nhau từ các thuộc tính. Tôi sử dụng tiền tố 'm' kiểu cũ, vì vậy nếu thuộc tính của tôi là "myProp", iVar của tôi sẽ là "mMyProp". Sau đó, tôi sử dụng @synthesize myProp = mMyProp để kết hợp cả hai.

Đây là một chút vụng về mà tôi thừa nhận và một chút cách gõ thừa, nhưng có vẻ như đáng để tôi có thể phân biệt rõ hơn một chút trong mã. Tất nhiên tôi vẫn có thể làm cho nó sai và gõ mMyProp = someObject, nhưng tôi hy vọng rằng tiền tố 'm' sẽ cảnh báo tôi về lỗi của tôi.Nó sẽ cảm thấy đẹp hơn nếu tôi chỉ có thể khai báo tài sản và để trình biên dịch/thời gian chạy làm phần còn lại, nhưng khi tôi có nhiều mã, bản năng ruột của tôi nói với tôi rằng tôi sẽ phạm sai lầm theo cách đó nếu tôi vẫn có để thực hiện theo các quy tắc thủ công cho init/dealloc.

Tất nhiên cũng có nhiều thứ khác tôi cũng có thể làm sai ...

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