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:
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 đó.)
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 @property
dòng định nghĩa một thuộc tính gọi là propertyName
kiể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.propertyName
bạn đang thực sự gọi các phương thức setter / getter trên đối tượng.
Các @synthesize
dò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 otherVarName
nếu bạn sử dụng cú pháp trong ý kiến).
Cùng với đó, @synthesize
bạ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 @property
dò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 @synthesiz
cá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 nonatomic
thuộ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ị retain
này sẽ tự động gửi release
đến các giá trị cũ của thuộc tính và retain
các giá trị mới. Điều này rất hữu ích.
Thứ hai là copy
sẽ 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. copy
Nê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. copy
sẽ 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à assign
chỉ đị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 readonly
thuộc tính để vô hiệu hóa bộ cài đặt cho thuộc tính.
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.
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.
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ư objc
hơ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
, nullable
vv
@implementation
muố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).