Sự khác biệt giữa ivars và thuộc tính trong Objective-C là gì


82

Sự khác biệt về ngữ nghĩa giữa 3 cách sử dụng ivars và thuộc tính này trong Objective-C là gì?

1.

@class MyOtherObject; 
@interface MyObject {
}
@property (nonatomic, retain) MyOtherObject *otherObj;

2.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}
@property (nonatomic, retain) MyOtherObject *otherObj;

3.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}

Câu trả lời:


57

Số 1 khác với hai số kia bằng cách khai báo trước lớp MyOtherObject để giảm thiểu lượng mã được trình biên dịch và trình liên kết nhìn thấy và cũng có khả năng tránh các tham chiếu vòng tròn. Nếu bạn làm theo cách này, hãy nhớ đặt #import vào tệp .m.

Bằng cách khai báo @property, (và khớp với @synthesize trong tệp .m), bạn sẽ tự động tạo các phương thức truy cập với ngữ nghĩa bộ nhớ được xử lý theo cách bạn chỉ định. Quy tắc chung cho hầu hết các đối tượng là Giữ lại, nhưng NSStrings, ví dụ, nên sử dụng Sao chép. Trong khi Singletons và Delegates thường nên sử dụng Assign. Trình truy cập viết tay rất tẻ nhạt và dễ xảy ra lỗi, do đó, điều này giúp tiết kiệm rất nhiều lỗi đánh máy và lỗi câm.

Ngoài ra, việc khai báo một thuộc tính tổng hợp cho phép bạn gọi một phương thức truy cập bằng cách sử dụng ký hiệu dấu chấm như sau:

self.otherObj = someOtherNewObject; // set it  
MyOtherObject *thingee = self.otherObj; // get it 

Thay vì cách truyền thông thường, thông báo:

[self setOtherObject:someOtherNewObject]; // set it
MyOtherObject *thingee = [self otherObj]; // get it 

Đằng sau hậu trường, bạn thực sự đang gọi một phương thức trông như thế này:

- (void) setOtherObj:(MyOtherObject *)anOtherObject {

    if (otherObject == anOtherObject) {
        return;  
    }

    MyOtherObject *oldOtherObject = otherObject; // keep a reference to the old value for a second
    otherObject = [anOtherObject retain]; // put the new value in  
    [oldOtherObject release]; // let go of the old object
} // set it

… Hoặc cái này

- (MyOtherObject *) otherObject {  
    return otherObject;
} // get it

Đau toàn bộ ở mông, phải. Bây giờ hãy làm điều đó cho mọi ivar trong lớp. Nếu bạn không làm đúng chính xác, bạn sẽ bị rò rỉ bộ nhớ. Tốt nhất hãy để trình biên dịch thực hiện công việc.

Tôi thấy rằng Số 1 không có ivar. Giả sử đó không phải là lỗi đánh máy, cũng không sao vì chỉ thị @property / @synthesize cũng sẽ khai báo một ivar cho bạn, ở đằng sau hậu trường. Tôi tin rằng điều này là mới cho Mac OS X - Snow Leopard và iOS4.

Số 3 không có các trình truy cập đó được tạo nên bạn phải tự viết chúng. Nếu bạn muốn các phương thức truy cập của mình có các tác dụng phụ, bạn thực hiện điệu nhảy quản lý bộ nhớ tiêu chuẩn, như được hiển thị ở trên, sau đó thực hiện bất kỳ công việc phụ nào bạn cần, bên trong phương thức trình truy cập. Nếu bạn tổng hợp một thuộc tính cũng như viết của riêng bạn , thì phiên bản của bạn sẽ được ưu tiên.

Tôi đã bao gồm tất cả mọi thứ?


Vâng cám ơn bạn rất nhiều! Một lưu ý mà tôi muốn thực hiện là nếu bạn loại bỏ pragma lớp chuyển tiếp trong # 1 và thay thế nó bằng #import "MyOtherObject", bạn sẽ gặp lỗi thời gian biên dịch, mặc dù không chắc chắn tại sao ....
ennuikiller

có lợi thế nào khi sử dụng cách tiếp cận số 2 so với cách tiếp cận số 1?
Greg

@Greg Method # 1 sẽ ngăn tham chiếu vòng tròn. Xem stackoverflow.com/questions/7221174/…
willc2

3
Câu trả lời tuyệt vời ngoại trừ một chút về ký hiệu dấu chấm. Bạn không cần phải tổng hợp thuộc tính để sử dụng nó cho ký hiệu dấu chấm. Trên thực tế, bạn không cần phải kê khai tài sản gì cả. Miễn là bạn có một setter và getter được khai báo (ví dụ setFoo:foo), bạn có thể sử dụng ký hiệu dấu chấm.
JeremyP

Đối với mức độ liên quan, nếu sử dụng ARC, việc tổng hợp được thực hiện tự động.
Sean Larkin

17

Ngày xưa bạn đã có ivars, và nếu bạn muốn cho một số lớp khác thiết lập hoặc đọc chúng thì bạn phải xác định một getter (tức là, -(NSString *)foo)và một setter (tức là, -(void)setFoo:(NSString *)aFoo;).

Những thuộc tính cung cấp cho bạn là setter và getter miễn phí (gần như!) Cùng với một ivar. Vì vậy, khi bạn xác định một thuộc tính ngay bây giờ, bạn có thể đặt nguyên tử (ví dụ: bạn có muốn cho phép nhiều hành động cài đặt từ nhiều luồng không), cũng như gán / giữ lại / sao chép ngữ nghĩa (nghĩa là, bộ cài đặt có sao chép giá trị mới không hoặc chỉ lưu giá trị hiện tại - quan trọng nếu một lớp khác đang cố gắng đặt thuộc tính chuỗi của bạn bằng một chuỗi có thể thay đổi có thể bị thay đổi sau này).

Đây là những gì @synthesizehiện. Nhiều người để tên ivar giống nhau, nhưng bạn có thể thay đổi nó khi bạn viết báo cáo tổng hợp của mình (nghĩa @synthesize foo=_foo;là đặt tên ivar _foocho thuộc tính foo, vì vậy nếu bạn muốn đọc hoặc viết thuộc tính này và bạn không sử dụng self.foo, bạn sẽ phải sử dụng _foo = ...- nó chỉ giúp bạn bắt các tham chiếu trực tiếp đến ivar nếu bạn chỉ muốn đi qua setter và getter).

Đối với Xcode 4.6, bạn không cần phải sử dụng @synthesizecâu lệnh - trình biên dịch sẽ tự động thực hiện và theo mặc định sẽ thêm tên của ivar vào _.


1
Cần lưu ý rằng tính nguyên tử của một thuộc tính không đảm bảo an toàn cho luồng .
jscs

Vì vậy, nếu tôi có một ivar là nguyên tử, ý bạn là trong khi setter đang thiết lập nó hoặc getter đang nhận nó, một luồng khác bắt đầu và cố gắng làm một trong hai, rằng tất cả đều bị phá hủy? Vậy thì điểm của nguyên tử là gì? Sự hiểu biết của tôi là nguyên tử ít nhất đảm bảo rằng nếu bạn đặt ivar, nó sẽ được đặt, số lượng giữ lại của nó là phù hợp, v.v. Nếu không thì tại sao lại là nguyên tử? [Không phải nó giải quyết được tất cả các vấn đề chỉ ngăn bạn không bị phát hiện]
David H

2
Bạn được đảm bảo nhận được một đối tượng hợp lệ, toàn bộ - getter sẽ không trả về một đối tượng đang trong quá trình được phân bổ - nhưng nếu một luồng khác đang sử dụng setter, bạn có thể nhận được giá trị từ trước hoặc sau đó. Chỉ định điều đó phải được xử lý bên ngoài getters và setters. Nói cách khác, sẽ không có luồng nào bị gián đoạn trong quá trình hoạt động getter hoặc setter, nhưng thứ tự của các hoạt động không được xác định (ở cấp độ này, AFAIK) không được xác định.
jscs

Chà, tôi cho rằng nhận xét ban đầu của bạn đã đặt sai chỗ - tính nguyên tử được tôn vinh, chỉ là việc truy cập thông qua các chuỗi có thể dẫn đến một loạt vấn đề - do đó, mọi ivar tôi từng tuyên bố là nguyên tử và nếu có các chuỗi liên quan thì đồng thời được xử lý ở nơi khác.
David H
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.