Khi nào tôi nên sử dụng @synthesize một cách rõ ràng?


81

Theo như tôi biết, kể từ XCode 4.4, @synthesizesẽ tự động tạo các trình truy cập thuộc tính. Nhưng vừa rồi tôi đã đọc một đoạn mã mẫu về NSUndoManager, và trong đoạn mã đó, nó nhận thấy rằng đoạn mã @synthesizeđược thêm vào một cách rõ ràng. Giống:

@interface RootViewController  ()

@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, strong) NSUndoManager *undoManager;

@end

@implementation RootViewController
//Must explicitly synthesize this
@synthesize undoManager;

Bây giờ tôi đang cảm thấy khó hiểu ... Khi nào tôi nên thêm @synthesizemột cách rõ ràng vào mã của mình?


1
Mã mẫu có thể đã cũ. Về cơ bản, sử dụng nó trừ khi nó biến thành một vấn đề (ví dụ, tài sản trong các đại biểu sẽ không được tự động tổng hợp)
borrrden

1
Hãy thử bình luận ra @sythesize. Nếu mã vẫn hoạt động thì không cần thiết.
ThomasW

Câu trả lời:


171

Có rất nhiều câu trả lời, nhưng cũng có một sự nhầm lẫn lớn. Tôi sẽ cố gắng đặt một số thứ tự (hoặc tăng sự lộn xộn, chúng ta sẽ xem ...)

  1. Hãy ngừng nói về Xcode. Xcode là một IDE . clang là một trình biên dịch . Tính năng mà chúng ta đang thảo luận này được gọi là tự động tổng hợp các thuộc tính và nó là một phần mở rộng ngôn ngữ Objective-C được hỗ trợ bởi clang , là trình biên dịch mặc định được sử dụng bởi Xcode.
    Chỉ cần nói rõ, nếu bạn chuyển sang gcc trong Xcode, bạn sẽ không được hưởng lợi từ tính năng này (bất kể từ phiên bản Xcode.) Theo cách tương tự nếu bạn sử dụng trình soạn thảo văn bản và biên dịch bằng cách sử dụng clang từ dòng lệnh, bạn sẽ.

  2. Nhờ tự động tổng hợp, bạn không cần phải tổng hợp rõ ràng thuộc tính vì nó sẽ được trình biên dịch tổng hợp tự động như

    @synthesize propertyName = _propertyName
    

    Tuy nhiên, một số ngoại lệ tồn tại:

    • thuộc tính readwrite với getter và setter tùy chỉnh

      khi cung cấp cả triển khai tùy chỉnh getter và setter, thuộc tính sẽ không được tổng hợp tự động

    • thuộc tính chỉ đọc với getter tùy chỉnh

      khi cung cấp triển khai getter tùy chỉnh cho thuộc tính chỉ đọc, điều này sẽ không được tổng hợp tự động

    • @dynamic

      khi sử dụng @dynamic propertyName, thuộc tính sẽ không được tổng hợp tự động (khá rõ ràng, vì @dynamic@synthesizeloại trừ lẫn nhau)

    • thuộc tính được khai báo trong @protocol

      khi tuân theo một giao thức, bất kỳ thuộc tính nào mà giao thức xác định sẽ không được tổng hợp tự động

    • thuộc tính được khai báo trong một danh mục

      đây là trường hợp mà @synthesizechỉ thị không được trình biên dịch tự động chèn vào, nhưng các thuộc tính này cũng không thể được tổng hợp theo cách thủ công. Trong khi các danh mục có thể khai báo các thuộc tính, chúng hoàn toàn không thể được tổng hợp, vì các danh mục không thể tạo ivars. Để hoàn thiện, tôi sẽ nói thêm rằng vẫn có thể giả mạo tổng hợp thuộc tính bằng thời gian chạy Objective-C .

    • thuộc tính ghi đè (mới kể từ clang-600.0.51, vận chuyển với Xcode 6, cảm ơn Marc Schlüpmann)

      khi bạn ghi đè một thuộc tính của lớp cha, bạn phải tổng hợp nó một cách rõ ràng

Cần lưu ý rằng việc tổng hợp một thuộc tính sẽ tự động tổng hợp ivar hỗ trợ, vì vậy nếu thiếu phần tổng hợp thuộc tính, ivar cũng sẽ bị thiếu, trừ khi được khai báo rõ ràng.

Ngoại trừ ba trường hợp cuối cùng, triết lý chung là bất cứ khi nào bạn chỉ định thủ công tất cả thông tin về một thuộc tính (bằng cách triển khai tất cả các phương thức của trình truy cập hoặc sử dụng @dynamic), trình biên dịch sẽ cho rằng bạn muốn có toàn quyền kiểm soát thuộc tính và nó sẽ tắt chức năng tự động tổng hợp trên nó.

Ngoài các trường hợp được liệt kê ở trên, cách sử dụng duy nhất khác của một sự rõ ràng @synthesizesẽ là chỉ định một tên ivar khác. Tuy nhiên, các quy ước rất quan trọng, vì vậy lời khuyên của tôi là luôn sử dụng cách đặt tên mặc định.


Nếu người hỏi ban đầu vẫn còn ở đây, tôi nghĩ câu trả lời này nên được chấp nhận. Nó là đầy đủ nhất.
Steven Fisher

3
Các thuộc tính được chỉ định trong một danh mục cũng không được tổng hợp tự động, theo như tôi nhớ. (Lý do cơ bản là bạn không thể thêm một biến phiên bản trong một danh mục.)
Martin R

@MartinR, điểm tốt. Chúng không được tổng hợp tự động, nhưng chúng cũng không thể được tổng hợp theo cách thủ công vì @synthesizetrong danh mục bị cấm (không có ivar trong danh mục, như bạn đã lưu ý). Tôi sẽ thêm một ghi chú.
Gabriele Petronella

Nhưng câu trả lời này không giải quyết được câu hỏi cụ thể: KHI NÀO nên sử dụng @synthesize? Luôn luôn, không bao giờ, chỉ khi một số điều kiện được đáp ứng?
Jeff

21

Nếu bạn không sử dụng một cách rõ ràng @synthesize, trình biên dịch sẽ hiểu thuộc tính của bạn giống như cách bạn đã viết

@synthesize undoManager=_undoManager;

thì bạn sẽ có thể viết vào mã của mình những thứ như:

[_undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Đây là quy ước chung.

nếu bạn viết

@synthesize undoManager;

bạn sẽ có :

[undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Cá nhân tôi ngừng sử dụng @synthesize, vì nó không còn bắt buộc nữa. Đối với tôi, lý do duy nhất để sử dụng @synthesizelà liên kết an iVarvới a @property. Nếu bạn muốn tạo getter và setter cụ thể cho nó. Nhưng trong đoạn mã đã cho không có iVar, tôi nghĩ rằng điều này @synthesizelà vô ích. Nhưng bây giờ tôi nghĩ câu hỏi mới là "Khi nào sử dụng iVar?", Và tôi không có câu trả lời nào khác ngoài "không bao giờ" cho câu hỏi này!


2
Đồng ý, tôi đã ngừng sử dụng @synthesizevì không có lý do gì để làm điều đó nữa. Ngoài ra, dấu gạch dưới hàng đầu đóng vai trò như một lá cờ trực quan đẹp để cho bạn biết bạn đang làm việc xung quanh mạng an toàn quản lý bộ nhớ và phân luồng mà các thuộc tính cung cấp (bạn nên bỏ qua initdealloccác phương thức).
BergQuester

1
Câu hỏi là, khi nào bạn cần tổng hợp một cách rõ ràng. Bạn đã không trả lời điều đó ở tất cả.
Fogmeister

1
Hôm nay tôi phát hiện ra rằng điều này không hoàn toàn đúng. Nếu không có @synthesize, bạn chịu trách nhiệm tạo biến phiên bản sao lưu. Với nó, trình biên dịch sẽ làm điều đó cho bạn.
Steven Fisher

1
Và tôi đã không bỏ phiếu cho bất kỳ ai cho phát hiện này. :)
Steven Fisher

1
-1 bạn hoàn toàn thiếu các trường hợp mà bạn cần một tổng hợp rõ ràng. Ngoài ra, đây là một tính năng biên dịch, không phải là một Xcode.
Gabriele Petronella

14

Khi nào tôi nên thêm @synthesizerõ ràng vào mã của mình?

Nói chung, nếu nó được yêu cầu: Bạn có thể sẽ không bao giờ gặp trường hợp cần thiết.

Tuy nhiên, có một trường hợp bạn có thể thấy nó hữu ích.

Giả sử bạn đang viết cả getter và setter tùy chỉnh, nhưng muốn một biến thể hiện để hỗ trợ nó. (Đối với thuộc tính nguyên tử, điều này đơn giản như muốn có một bộ định tuyến tùy chỉnh: trình biên dịch sẽ viết một bộ định tuyến nếu bạn chỉ định một bộ định vị cho một thuộc tính monatomic, nhưng không phải một thuộc tính nguyên tử.)

Xem xét điều này:

@interface MyObject:NSObject
@property (copy) NSString *title;
@end

@implementation MyObject

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Điều này sẽ không hoạt động, bởi vì _titlekhông tồn tại. Bạn đã chỉ định cả getter hoặc setter, vì vậy Xcode (chính xác) không tạo biến phiên bản hỗ trợ cho nó.

nhập mô tả hình ảnh ở đây

Bạn có hai lựa chọn để làm cho nó tồn tại. Bạn có thể thay đổi @implementationthành này:

@implementation MyObject {
    NSString *_title;
}

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Hoặc thay đổi nó thành thế này:

@implementation MyObject

@synthesize title = _title;

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Nói cách khác, mặc dù tổng hợp dành cho các mục đích thực tế không bao giờ cần thiết *, nó có thể được sử dụng để xác định các biến phiên bản hỗ trợ thuộc tính khi bạn cung cấp getter / setter. Bạn có thể quyết định biểu mẫu nào ở đây bạn muốn sử dụng.

Trước đây, tôi đã ưu tiên chỉ định biến cá thể trong @implementation {}, nhưng bây giờ tôi nghĩ rằng @synthesizetuyến đường là lựa chọn tốt hơn vì nó loại bỏ kiểu thừa và liên kết rõ ràng biến sao lưu với thuộc tính:

  1. Thay đổi kiểu của thuộc tính và kiểu của biến thể hiện thay đổi.
  2. Thay đổi định tính lưu trữ của nó (ví dụ: làm cho nó yếu thay vì mạnh hoặc mạnh thay vì yếu) và định tính lưu trữ thay đổi.
  3. Xóa hoặc đổi tên thuộc tính và @synthesizesẽ tạo ra lỗi trình biên dịch. Bạn sẽ không kết thúc với các biến cá thể lạc chỗ.

* -Tôi biết một trường hợp cần thiết, liên quan đến việc phân chia chức năng giữa các danh mục trong nhiều tệp. Và tôi sẽ không ngạc nhiên nếu Apple sửa lỗi này, hoặc thậm chí đã có.


Bạn có chắc không? Bạn thậm chí đã thử? Tôi đã viết nhiều bộ cài đặt và bộ định tuyến tùy chỉnh và KHÔNG BAO GIỜ tổng hợp các thuộc tính của tôi. (Và tôi đã sử dụng ví dụ đầu tiên của bạn, mà bạn đã nói là không hoạt động)
Marc

Vâng tôi chắc chắn. Mã đã được sao chép từ một dự án mới. Nó sẽ không hoạt động trên các phiên bản Xcode gần đây trừ khi bạn chỉ định nonatomic trên thuộc tính.
Steven Fisher

1
vâng, điều đó đúng .. nhưng 99% thời gian tài sản của bạn là không có giải phẫu. Vì vậy, chỉ trong trường hợp hiếm hoi mà thuộc tính của bạn là nguyên tử và bạn muốn một getter / setter tùy chỉnh mà bạn thực sự cần tổng hợp.
Marc

Nếu bạn cung cấp một triển khai tùy chỉnh cho cả setter và getter, thì trình biên dịch sẽ cho rằng bạn đang kiểm soát thuộc tính và nó sẽ không tổng hợp nó cho bạn.
Gabriele Petronella

2
Tôi đồng ý với bạn ở đó, mặc dù tôi có thể hiểu tại sao Apple ban đầu nghĩ đó là một ý tưởng hay. Điều đau đớn thực sự là nó atomickhông được thêm vào như một đặc tính cho đến sau này. Bây giờ nó ở đó, bạn có thể thấy cờ cảnh báo CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESthú vị.
Steven Fisher

7

Được rồi, khi bạn tạo thuộc tính ...

@property NSString *name;

Xcode sẽ tự động tổng hợp một iVar như thể bạn đã viết ...

@synthesize name = _name;

Điều này có nghĩa là bạn có thể truy cập tài sản bằng ...

self.name;
// or
_name;

Hoặc sẽ hoạt động nhưng chỉ self.namethực sự sử dụng các phương thức truy cập.

Chỉ có một lần tính năng tự động tổng hợp không hoạt động: Nếu bạn ghi đè nhưng phương thức setter VÀ getter thì bạn sẽ cần phải tổng hợp iVar.

Bạn sẽ ổn nếu bạn chỉ ghi đè bộ cài hoặc nếu bạn chỉ ghi đè bộ nhận. Nhưng nếu bạn làm cả hai thì trình biên dịch sẽ không hiểu nó và bạn sẽ cần phải tổng hợp thủ công.

Như một quy tắc của ngón tay cái mặc dù.

Đừng làm iVars. Chỉ cần sử dụng tài sản. Đừng tổng hợp nó.


1
Có nhiều trường hợp mà quá trình tự tổng hợp sẽ không được thực hiện. Kiểm tra câu trả lời của tôi.
Gabriele Petronella

@GabrielePetronella bạn có thể liệt kê ngắn gọn những trường hợp đó cho người đọc bình thường không?
Dan Rosenstark

@DanRosenstark điều này không liên quan đến bất kỳ ai lập trình bây giờ. Bạn thực sự nên sử dụng Swift. Và TBH tôi nghĩ rằng toàn bộ điều tổng hợp thậm chí không còn là một thứ trong ObjC nữa. Trừ khi bạn đang làm việc trên cơ sở mã đã hơn 5 năm tuổi.
Fogmeister

@Fogmeister vâng, nó vẫn là một vấn đề trong trường hợp bạn đề cập trong câu trả lời của mình (nơi bạn ghi đè cả setter và getter). Và vì Objective-C không "phù hợp với bất kỳ ai lập trình bây giờ", vui lòng gọi cho công việc trong ngày của tôi và nói với họ. Cũng nói với Facebook, Google và Apple, những người đang sử dụng Objective-C trong nội bộ để đạt được hiệu quả tuyệt vời.
Dan Rosenstark

Tôi chắc chắn điều này là đúng mà không Quora, nhưng dù sao: quora.com/...
Dan Rosenstark

1

Tổng hợp thuộc tính là bắt buộc khi một thuộc tính được khai báo trong một giao thức. Nó sẽ không được tổng hợp tự động trong một giao diện triển khai.


đúng, nhưng đó chỉ là một trường hợp. Bạn có thể muốn giải thích thêm.
Gabriele Petronella

@GabrielePetronella Đó là người duy nhất tôi có thể nghĩ đến trong giờ muộn này. =]
Leo Natan

0

Cảm ơn vì đã làm rõ điều đó. Tôi đã có một vấn đề tương tự.

@synthesize firstAsset, secondAsset, audioAsset;
@synthesize activityView;

Vì vậy, bây giờ, sau khi nhận xét chúng, tôi đã xem xét và thay thế mỗi lần xuất hiện, chẳng hạn như

self.firstAsset Có vẻ như tôi cũng có thể sử dụng firstAsset, nhưng tôi thấy mình không nhìn thấy " " quá thường xuyên.


-1

Xcode không yêu cầu một @synthesizekhai báo rõ ràng .

Nếu bạn không viết @synthesizenó giống như làm:

@synthesize manager = _manager;

Mã mẫu có thể đã cũ. Họ sẽ cập nhật nó sớm.

Bạn có thể truy cập các thuộc tính của mình như:

[self.manager function];

Đây là quy ước được đề xuất của Apple. Tôi làm theo nó, và tôi khuyên bạn cũng nên làm như vậy!


2
Tuyên bố [_manager function]sẽ KHÔNG truy cập thuộc tính, thay vào đó, nó sẽ truy cập trực tiếp ivar bên dưới .
CouchDeveloper

@CouchDeveloper Nếu bạn muốn nitpicking, bạn nên chính xác. Tài sản bao gồm một số thứ, bao gồm cả ivar. Vì vậy, tốt hơn là nói rằng [_manager function]không sử dụng người truy cập của tài sản.
Nikolai Ruhe

@CouchDeveloper - Cảm ơn vì điều đó! Đã sửa nó! :)
Sam Fischer

1
@NikolaiRuhe Bạn nói đúng Nikolai, lẽ ra tôi phải nói chính xác hơn. ;)
CouchDeveloper
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.