Có sự khác biệt giữa “biến phiên bản” và “thuộc tính” trong Objective-c không?


82

Có sự khác biệt giữa "biến cá thể" và "thuộc tính" trong Objective-c không?

Tôi không chắc lắm về điều này. Tôi nghĩ rằng "thuộc tính" là một biến cá thể có các phương thức của trình truy cập, nhưng tôi có thể nghĩ sai.

Câu trả lời:


84

Thuộc tính là một khái niệm trừu tượng hơn. Một biến thể hiện theo nghĩa đen chỉ là một vùng lưu trữ, giống như một vùng trong một cấu trúc. Thông thường các đối tượng khác không bao giờ được phép truy cập trực tiếp vào chúng. Mặt khác, thuộc tính là một thuộc tính của đối tượng của bạn có thể được truy cập (nghe có vẻ mơ hồ và đúng ra là như vậy). Thông thường, một thuộc tính sẽ trả về hoặc đặt một biến phiên bản, nhưng nó có thể sử dụng dữ liệu từ một số hoặc không có. Ví dụ:

@interface Person : NSObject {
    NSString *name;
}

    @property(copy) NSString *name;
    @property(copy) NSString *firstName;
    @property(copy) NSString *lastName;
@end

@implementation Person
    @synthesize name;

    - (NSString *)firstName {
        [[name componentsSeparatedByString:@" "] objectAtIndex:0];
    }
    - (NSString *)lastName {
        [[name componentsSeparatedByString:@" "] lastObject];
    }
    - (NSString *)setFirstName:(NSString *)newName {
        NSArray *nameArray = [name componentsSeparatedByString:@" "];
        NSArray *newNameArray [[NSArray arrayWithObjects:newName, nil] arrayByAddingObjectsFromArray:[nameArray subarrayWithRange:NSMakeRange(1, [nameArray size]-1)]];
        self.name = [newNameArray componentsJoinedByString:@" "];
    }
    - (NSString *)setLastName:(NSString *)newName {
        NSArray *nameArray = [name componentsSeparatedByString:@" "];
        NSArray *newNameArray [[nameArray subarrayWithRange:NSMakeRange(0, [nameArray size]-2)] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:newName, nil]];
        self.name = [newNameArray componentsJoinedByString:@" "];
    }
@end

(Lưu ý: Đoạn mã trên có lỗi ở chỗ nó giả định tên đã tồn tại và có ít nhất hai thành phần (ví dụ: "Bill Gates" thay vì chỉ "Gates"). Tôi cảm thấy rằng việc sửa chữa những giả định đó sẽ làm cho điểm thực sự của mã ít rõ ràng hơn, vì vậy tôi chỉ chỉ ra ở đây để không ai vô tội lặp lại những sai lầm đó.)


4
Cách mà tôi đang xem thuộc tính là một phương tiện cung cấp / hạn chế quyền truy cập vào các biến cá thể cho các đối tượng bên ngoài. Giống như khái niệm công khai / riêng tư trong các ngôn ngữ khác?
nguyên mẫu

"Thông thường các đối tượng khác không bao giờ được cho là có thể truy cập trực tiếp vào chúng" ý bạn là gì? Ngoài ra câu trả lời của bạn có được cập nhật với mục tiêu hiện đại-c không?
Mật ong,

1
@Honey Tôi nghĩ anh ấy đang đề cập đến khái niệm đóng gói và làm theo các phương pháp hay nhất. Các đối tượng khác sẽ không thể truy cập trực tiếp hoặc sửa đổi ivar. Bằng cách kiểm soát truy cập ivar thông qua các thuộc tính, chúng tôi có thể chặn các cuộc gọi đó trước khi chúng có khả năng ảnh hưởng đến ivar. Xem ở đây để biết thêm: en.wikipedia.org/wiki/Encapsulation_(computer_programming)
user3344977

33

Thuộc tính là một cách thân thiện để triển khai getter / setter cho một số giá trị, với các tính năng và cú pháp hữu ích bổ sung. Một thuộc tính có thể được hỗ trợ bởi một biến thể hiện, nhưng bạn cũng có thể xác định getter / setter để làm điều gì đó năng động hơn một chút, ví dụ: bạn có thể xác định thuộc tính LowerCase trên một chuỗi sẽ tự động tạo ra kết quả thay vì trả về giá trị của một số thành viên Biến đổi.

Đây là một ví dụ:

// === In your .h ===

@interface MyObject {
    NSString *propertyName;

}

// ...

@property (nonatomic, retain) NSString *propertyName;

// === In your .m @implementation ===

@synthesize propertyName /* = otherVarName */;

Các @propertydòng định nghĩa một thuộc tính gọi là propertyNamekiểu NSString *. Điều này có thể được lấy / đặt bằng cú pháp sau:

myObject.propertyName = @"Hello World!";
NSLog("Value: %@", myObject.propertyName);

Khi bạn gán hoặc đọc từ myObject.propertyNamebạn đang thực sự gọi các phương thức setter / getter trên đối tượng.

Các @synthesizedòng cho trình biên dịch để tạo ra những phương thức getter / setters cho bạn, bằng cách sử dụng biến thành viên có cùng tên của thuộc tính để lưu trữ các giá trị (hoặc otherVarNamenếu bạn sử dụng cú pháp trong ý kiến).

Cùng với đó, @synthesizebạn vẫn có thể ghi đè một trong những getter / setters bằng cách xác định của riêng bạn. Quy ước đặt tên cho các phương thức này là setPropertyName:dành cho bộ cài đặt và propertyName(hoặc getPropertyName, không phải là tiêu chuẩn) cho bộ nhận. Cái khác vẫn sẽ được tạo cho bạn.

Trong @propertydòng của bạn, bạn có thể xác định một số thuộc tính trong parens cho thuộc tính có thể tự động hóa những thứ như an toàn luồng và quản lý bộ nhớ. Theo mặc định, một thuộc tính là nguyên tử có nghĩa là trình biên dịch sẽ bao bọc @synthesizcác cuộc gọi get / set bằng các khóa thích hợp để ngăn chặn các vấn đề đồng thời. Bạn có thể chỉ định nonatomicthuộc tính để tắt tính năng này (ví dụ: trên iPhone, bạn muốn đặt hầu hết các thuộc tính thành mặc định nonatomic).

Có 3 giá trị thuộc tính kiểm soát việc quản lý bộ nhớ cho bất kỳ bộ cài đặt nào @synthesized. Đầu tiên là giá trị retainnày sẽ tự động gửi releaseđến các giá trị cũ của thuộc tính và retaincác giá trị mới. Điều này rất hữu ích.

Thứ hai là copysẽ tạo một bản sao của bất kỳ giá trị nào được truyền vào thay vì giữ lại chúng. copyNên sử dụng cho NSString vì người gọi có thể chuyển vào NSMutableString và thay đổi nó từ bên dưới bạn. copysẽ tạo một bản sao mới của đầu vào mà chỉ bạn mới có quyền truy cập.

Thứ ba là assignchỉ định một con trỏ thẳng mà không gọi giữ lại / phát hành trên đối tượng cũ hoặc mới.

Cuối cùng, bạn cũng có thể sử dụng readonlythuộc tính để vô hiệu hóa bộ cài đặt cho thuộc tính.


1
Có lợi ích gì khi khai báo biến cá thể và thuộc tính (ví dụ: propertyName) không? Khai báo bên trong giao diện là không cần thiết nếu bạn khai báo một thuộc tính cho cùng một biến, đúng không? Điều này thực sự tiết kiệm trên các dòng mã, trừ khi có thứ gì đó tôi đang thiếu ..
whyoz

6

Tôi sử dụng các thuộc tính cho phần giao diện - nơi đối tượng giao diện với các đối tượng khác và các biến cá thể là những thứ mà bạn cần bên trong lớp của mình - không ai ngoài bạn phải nhìn thấy và thao tác chúng.


3

Theo mặc định, một thuộc tính readwrite sẽ được hỗ trợ bởi một biến thể hiện, biến này sẽ lại được trình biên dịch tổng hợp tự động.

Biến thể hiện là một biến tồn tại và giữ giá trị của nó trong thời gian tồn tại của đối tượng. Bộ nhớ được sử dụng cho các biến cá thể được cấp phát khi đối tượng được tạo lần đầu tiên (thông qua cấp phát) và được giải phóng khi đối tượng được phân bổ.

Trừ khi bạn chỉ định khác, biến phiên bản tổng hợp có cùng tên với thuộc tính, nhưng có tiền tố gạch dưới. Ví dụ: đối với thuộc tính có tên firstName, biến phiên bản tổng hợp sẽ được gọi là _firstName.


2

Trước đây mọi người sử dụng thuộc tính công khai và ivars để sử dụng riêng tư, nhưng kể từ vài năm trước, bạn cũng có thể xác định các thuộc tính @implementationđể sử dụng chúng một cách riêng tư. Nhưng tôi vẫn sẽ sử dụng ivars khi có thể, vì có ít chữ cái hơn để nhập và nó chạy nhanh hơn theo bài viết này . Nó có ý nghĩa vì các thuộc tính có nghĩa là "nặng": chúng phải được truy cập từ getters / setters được tạo hoặc những người được viết thủ công.

Tuy nhiên, trong các mã gần đây từ Apple, ivars không được sử dụng nữa. Tôi đoán vì nó giống như objchơn C/C++, cộng với nó dễ dàng hơn để sử dụng các thuộc tính với assign, nullablevv


Tôi đoán rằng việc sử dụng các thuộc tính trong Apples @implementationmuốn thể hiện những điểm tương đồng với Swift. Tuy nhiên, tôi cũng thích các biến sao lưu để không lãng phí một cuộc gọi hàm ảo để tra cứu một trường đơn giản của lớp riêng của tôi (và điều đó xảy ra khi thuộc tính được truy cập).
Leo
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.