IBOutlets nên mạnh hay yếu theo ARC?


551

Tôi đang phát triển dành riêng cho iOS 5 bằng ARC. Có nên IBOutlets để UIViews (và các lớp con) có stronghay weak?

Sau đây là

@property (nonatomic, weak) IBOutlet UIButton *button;

Sẽ thoát khỏi tất cả những điều này:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

Có bất kỳ vấn đề làm điều này? Các mẫu đang sử dụng strongnhư là các thuộc tính được tạo tự động được tạo khi kết nối trực tiếp với tiêu đề từ trình chỉnh sửa 'Trình tạo giao diện', nhưng tại sao? Các UIViewControllerđã có một strongtham chiếu đến nó viewmà vẫn giữ được subviews của nó.


11
Như một lưu ý, IBOutletCollection()không được weak, nếu không nó sẽ trả về nil.
ohho

Xcode 8.2.1 sử dụng yếu khi tạo IBOutlets thông qua trình tạo giao diện. Tuy nhiên nhiều câu trả lời ở đây trên SO khuyên nên sử dụng mạnh mẽ.
neoneye

1
@neoneye Tôi vừa thử với xcode 8.3.2 kéo từ bảng phân cảnh sang tập tin nhanh và nó mặc định làstrong
CupawnTae

Câu trả lời:


252

Dòng khuyến cáo thực hành tốt nhất từ Apple là cho IBOutlets là mạnh trừ yếu là đặc biệt cần thiết để tránh một giữ lại chu kỳ. Như Johannes đã đề cập ở trên, điều này đã được nhận xét trong phiên "Triển khai thiết kế giao diện người dùng trong Trình tạo giao diện" từ WWDC 2015, nơi một Kỹ sư của Apple cho biết:

Và tùy chọn cuối cùng tôi muốn chỉ ra là loại lưu trữ, có thể mạnh hoặc yếu. Nói chung, bạn nên làm cho ổ cắm của mình mạnh mẽ, đặc biệt nếu bạn đang kết nối một ổ cắm với một khung nhìn phụ hoặc với một ràng buộc không phải lúc nào cũng được giữ lại bởi hệ thống phân cấp chế độ xem. Lần duy nhất bạn thực sự cần làm cho một ổ cắm yếu là nếu bạn có chế độ xem tùy chỉnh tham chiếu thứ gì đó sao lưu phân cấp chế độ xem và nói chung không được khuyến nghị.

Tôi đã hỏi về điều này trên Twitter với một kỹ sư trong nhóm IB và anh ấy xác nhận rằng mạnh mẽ phải là mặc định và các tài liệu dành cho nhà phát triển đang được cập nhật.

https://twitter.com/_danielhall/status/620716996326350848 https://twitter.com/_danielhall/status/620717252216623104


33
Điều này có thực sự đúng hay là câu trả lời với hơn 300 câu trả lời đúng? Tôi nhận thấy rằng InterfaceBuilder theo mặc định sử dụng yếu khi bạn kéo Ctrl từ bảng phân cảnh sang .h
Arunabh Das

4
Người có hơn 400 phiếu bầu là đúng, nhưng đã lỗi thời. Vì viewDidUnload của iOS 6 không được gọi, nên không có lợi ích gì khi có các cửa hàng yếu.
kjam

7
@kjam có lợi ích. Trước hết, bạn không nên giữ một tài liệu tham khảo mạnh mẽ cho thứ gì đó bạn không tạo ra. Thứ hai, hiệu suất đạt được là không đáng kể. Đừng vi phạm các thực tiễn tốt nhất trong lập trình đơn giản chỉ vì một số người, thậm chí là một người có địa vị tốt, cho biết việc này nhanh hơn 10 micro giây. Mã ý định rõ ràng, đừng cố chơi trình biên dịch tối ưu hóa. Chỉ mã cho hiệu suất khi nó đã được đo trong một trường hợp cụ thể là một vấn đề.
Cameron Lowell Palmer

5
Hãy để tôi không đồng ý với bạn. 'Giữ một tham chiếu mạnh mẽ đến thứ mà bạn không tạo ra' xảy ra mọi lúc trong Objective-C. Đó là lý do tại sao có một tham chiếu đếm , chứ không phải sau đó một chủ sở hữu duy nhất. Bạn có bất kỳ tài liệu tham khảo để sao lưu đề xuất này? Bạn có thể vui lòng liệt kê các lợi ích khác của các cửa hàng yếu?
kjam

4
Dưới đây là video WWDC được đề cập trong câu trả lời developer.apple.com/ideo/play/wwdc2015/407/?time=1946
petrsyn

450

CẢNH BÁO, TRẢ LỜI NGOÀI TRỜI : câu trả lời này không cập nhật theo WWDC 2015, để có câu trả lời đúng, hãy tham khảo câu trả lời được chấp nhận (Daniel Hall) ở trên. Câu trả lời này sẽ ở lại để ghi lại.


Tóm tắt từ thư viện nhà phát triển :

Từ góc độ thực tế, trong các cửa hàng iOS và OS X nên được xác định là thuộc tính khai báo. Các cửa hàng nói chung nên yếu, ngoại trừ những người từ Chủ sở hữu tệp đến các đối tượng cấp cao nhất trong tệp nib (hoặc, trong iOS, cảnh phân cảnh) phải mạnh mẽ. Do đó, các cửa hàng mà bạn tạo sẽ thường yếu, bởi vì:

  • Các cửa hàng mà bạn tạo, ví dụ, các khung nhìn của khung nhìn của trình điều khiển khung nhìn hoặc cửa sổ của trình điều khiển cửa sổ, là các tham chiếu tùy ý giữa các đối tượng không ngụ ý quyền sở hữu.

  • Các ổ cắm mạnh thường được chỉ định bởi các lớp khung (ví dụ: ổ cắm khung nhìn của UIViewContoder hoặc ổ cắm cửa sổ của NSWindowControll).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;

10
Làm thế nào bạn có được liên kết "thư viện nhà phát triển" để chuyển đến phần cụ thể của trang apple doc? Bất cứ khi nào tôi liên kết đến tài liệu apple, nó luôn liên kết đến đầu trang (ngay cả khi nội dung quan tâm nằm ở giữa trang). Cảm ơn.
bearMenez

68
Tôi đã sao chép liên kết từ khung điều hướng bên trái. : D
Alexsander Akers

27
"Ngoại trừ những người từ Chủ sở hữu tệp đến các đối tượng cấp cao nhất trong tệp nib (hoặc, trong iOS, cảnh trong bảng phân cảnh)" nghĩa là gì?
Văn Du Trần

16
@VanDuTran - có nghĩa là các đối tượng trong NIB ở cấp độ gốc, tức là bạn đã khởi tạo một chế độ xem khác trong đó không trực tiếp là một khung nhìn của khung nhìn chính, sau đó nó cần phải có một tham chiếu mạnh.
mattjgalloway

6
Cấp cao nhất có nghĩa là khi bạn nhìn vào ngòi, đối tượng sẽ xuất hiện trong danh sách bên trái. Hầu như tất cả các ngòi đều có một UIView trong đó - đây có thể là đối tượng cấp cao nhất duy nhất. Nếu bạn thêm các mục khác và chúng hiển thị trong danh sách, chúng là "đối tượng cấp cao nhất"
David H

50

Mặc dù tài liệu khuyến nghị sử dụng weaktrên các thuộc tính cho các cuộc phỏng vấn, vì iOS 6 có vẻ vẫn ổn để sử dụng strong(vòng loại quyền sở hữu mặc định) thay vào đó. Điều đó gây ra bởi sự thay đổi trong UIViewControllerquan điểm đó sẽ không được tải nữa.

  • Trước iOS 6, nếu bạn giữ các liên kết mạnh đến các chế độ xem của chế độ xem xung quanh, nếu chế độ xem chính của bộ điều khiển chế độ xem không được tải, thì các chế độ xem đó sẽ giữ các chế độ xem miễn là xung quanh bộ điều khiển xem.
  • Kể từ iOS 6, các lượt xem không được tải nữa, nhưng được tải một lần và sau đó bám xung quanh miễn là bộ điều khiển của chúng ở đó. Vì vậy, tài sản mạnh mẽ sẽ không quan trọng. Họ cũng sẽ không tạo chu kỳ tham chiếu mạnh, vì họ chỉ ra biểu đồ tham chiếu mạnh.

Điều đó nói rằng, tôi đang bị rách giữa việc sử dụng

@property (nonatomic, weak) IBOutlet UIButton *button;

@property (nonatomic) IBOutlet UIButton *button;

trong iOS 6 và sau:

  • Sử dụng weakrõ ràng rằng bộ điều khiển không muốn sở hữu nút.

  • Nhưng bỏ qua weakkhông làm tổn thương trong iOS 6 mà không xem tải xuống và ngắn hơn. Một số có thể chỉ ra rằng nó cũng nhanh hơn, nhưng tôi chưa gặp phải một ứng dụng quá chậm vì weak IBOutlets.

  • Không sử dụng weakcó thể được coi là một lỗi.

Điểm mấu chốt: Vì iOS 6, chúng tôi không thể hiểu sai điều này nữa miễn là chúng tôi không sử dụng chế độ xem tải. Đến giờ tiệc tùng. ;)


Đó là sự thật, nhưng bạn vẫn có thể muốn tự hủy tải chế độ xem. Trong trường hợp đó, bạn phải đặt tất cả các cửa hàng của mình thành nilthủ công.
siêu mã hóa

PS: weakrẻ hơn một chút trong ARM64: D
hypercrypt

Điều đó đúng, nếu bạn thực hiện xem tải, weakcác thuộc tính hoặc __weakbiến thể hiện là cách để đi. Tôi chỉ muốn chỉ ra rằng có ít khả năng xảy ra lỗi ở đây. Về weakviệc rẻ hơn trên arm64, tôi thậm chí chưa thấy vấn đề về hiệu năng thực tế với weak IBOutlets trên armv7. :)
Tammo Freese

Trong trường hợp đó, strongcó ý nghĩa là tốt. strongĐiều này chỉ có hại nếu bạn sử dụng chế độ xem dỡ tải nhưng những ai làm những ngày này? :)
Tammo Freese

2
@Rocotilos iPhone đầu tiên có RAM rất hạn chế. Nếu tôi nhớ lại chính xác, 128 MB, để lại khoảng 10 MB cho ứng dụng đang hoạt động. Có một dấu chân bộ nhớ nhỏ là rất quan trọng, do đó có chế độ xem tải. Điều đó đã thay đổi khi chúng ta ngày càng có nhiều RAM hơn và Apple đã tối ưu hóa UIViews trong iOS 6, để cảnh báo bộ nhớ, rất nhiều bộ nhớ có thể được giải phóng mà không cần tải xuống chế độ xem.
Tammo Freese

34

Tôi không thấy bất kỳ vấn đề với điều đó. Pre-ARC, tôi đã luôn tạo ra IBOutlets của mình assign, vì chúng đã được các giám sát viên của họ giữ lại. Nếu bạn tạo chúng weak, bạn không cần phải loại bỏ chúng trong viewDidUnload, như bạn chỉ ra.

Một cảnh báo: Bạn có thể hỗ trợ iOS 4.x trong một dự án ARC, nhưng nếu có, bạn không thể sử dụng weak, vì vậy bạn phải tạo ra chúng assign, trong trường hợp đó bạn vẫn muốn bỏ qua tham chiếu viewDidUnloadđể tránh một con trỏ lơ lửng. Đây là một ví dụ về lỗi con trỏ lơ lửng tôi đã gặp phải:

Một UIViewControll có UITextField cho mã zip. Nó sử dụng CLLocationManager để đảo ngược mã địa lý vị trí của người dùng và đặt mã zip. Đây là cuộc gọi lại của đại biểu:

-(void)locationManager:(CLLocationManager *)manager
   didUpdateToLocation:(CLLocation *)newLocation
          fromLocation:(CLLocation *)oldLocation {
    Class geocoderClass = NSClassFromString(@"CLGeocoder");
    if (geocoderClass && IsEmpty(self.zip.text)) {
        id geocoder = [[geocoderClass alloc] init];
        [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
            if (self.zip && IsEmpty(self.zip.text)) {
                self.zip.text = [[placemarks objectAtIndex:0] postalCode];
            }
        }];    
    }
    [self.locationManager stopUpdatingLocation];
}

Tôi thấy rằng nếu tôi loại bỏ chế độ xem này vào đúng thời điểm và không thực hiện self.zip viewDidUnload, thì cuộc gọi lại của đại biểu có thể ném ngoại lệ truy cập xấu vào self.zip.text.


4
Đó cũng là sự hiểu biết của tôi rằng weakcác thuộc tính không cần phải được ghi vào viewDidUnload. Nhưng tại sao mẫu của Apple để tạo ra các cửa hàng bao gồm một [self setMySubview:nil]?
Yang Meyer

3
Có trường hợp nào trong thế giới thực mà việc sử dụng mạnh / giữ lại cho IBOutlet của bạn có thể gây ra sự cố không? Hoặc nó chỉ là một khoản giữ lại dư thừa, có nghĩa là phong cách mã hóa xấu nhưng sẽ không ảnh hưởng đến mã của bạn?
Enzo Trần

1
Có một điều như một giữ lại dư thừa? Nếu có thêm một khoản giữ lại, điều đó sẽ khiến nó không được tính đúng, và do đó sẽ không được giải phóng ngay khi có thể vì có thêm một khoản giữ lại trong số tiền giữ lại của nó.
karlbecker_com

25

IBOutletnên mạnh mẽ, vì lý do hiệu suất. Xem Tham khảo bảng phân cảnh, IBOutlet mạnh, Dock cảnh trong iOS 9

Như đã giải thích trong đoạn này, các lối ra cho các cuộc phỏng vấn của khung nhìn của trình điều khiển khung nhìn có thể yếu, bởi vì các cuộc phỏng vấn này đã được sở hữu bởi đối tượng cấp cao nhất của tệp nib. Tuy nhiên, khi một Outlet được định nghĩa là một con trỏ yếu và con trỏ được đặt, ARC gọi hàm thời gian chạy:

id objc_storeWeak(id *object, id value);

Điều này thêm con trỏ (đối tượng) vào một bảng bằng cách sử dụng giá trị đối tượng làm khóa. Bảng này được gọi là bảng yếu. ARC sử dụng bảng này để lưu trữ tất cả các con trỏ yếu của ứng dụng của bạn. Bây giờ, khi giá trị đối tượng được giải phóng, ARC sẽ lặp lại trên bảng yếu và đặt tham chiếu yếu thành nil. Ngoài ra, ARC có thể gọi:

void objc_destroyWeak(id * object)

Sau đó, đối tượng không được đăng ký và objc_destroyWeak gọi lại:

objc_storeWeak(id *object, nil)

Việc giữ sách này được liên kết với một tài liệu tham khảo yếu có thể mất 2 lần 3 lần so với việc phát hành một tài liệu tham khảo mạnh. Vì vậy, một tham chiếu yếu giới thiệu một chi phí chung cho thời gian chạy mà bạn có thể tránh bằng cách chỉ cần xác định các cửa hàng là mạnh.

Kể từ Xcode 7, nó gợi ý strong

Nếu bạn xem phiên WWDC 2015 407 Thực hiện Thiết kế giao diện người dùng trong Trình tạo giao diện , nó sẽ gợi ý (bản sao từ http://asciiwwdc.com/2015/simes/407 )

Và tùy chọn cuối cùng tôi muốn chỉ ra là loại lưu trữ, có thể mạnh hoặc yếu.

Nói chung, bạn nên làm cho ổ cắm của mình mạnh mẽ, đặc biệt nếu bạn đang kết nối một ổ cắm với chế độ xem phụ hoặc với một ràng buộc không phải lúc nào cũng được giữ lại bởi hệ thống phân cấp chế độ xem.

Lần duy nhất bạn thực sự cần làm cho một ổ cắm yếu là nếu bạn có chế độ xem tùy chỉnh tham chiếu thứ gì đó sao lưu phân cấp chế độ xem và nói chung không được khuyến nghị.

Vì vậy, tôi sẽ chọn mạnh mẽ và tôi sẽ nhấp vào kết nối sẽ tạo ra ổ cắm của tôi.


1
Câu trả lời tuyệt vời giải thích lý do thực tế
sao-

Đó là tốt và tất cả nhưng tôi đã thấy rò rỉ đến từ nhận dạng cử chỉ được thực hiện trong bảng phân cảnh.
thibaut noah

1
Tôi không thể hiểu dòng này. "Lần duy nhất bạn thực sự cần làm cho một ổ cắm yếu là nếu bạn có chế độ xem tùy chỉnh tham chiếu thứ gì đó sao lưu phân cấp chế độ xem và nói chung điều đó không được khuyến nghị." Ví dụ nào?
dùng1872384

Tôi đã tính thời gian deinit mà yếu và mạnh mất, và nó hoàn toàn giống nhau.
touti

Nhưng trong swift đây là trường hợp nhiều hơn. Tài liệu tham khảo yếu nhanh hơn.
từ

20

Trong phát triển iOS, tải NIB hơi khác một chút so với phát triển Mac.

Trong phát triển Mac, IBOutlet thường là một tham chiếu yếu: nếu bạn có một lớp con NSViewCont kiểm soát, chỉ có chế độ xem cấp cao nhất sẽ được giữ lại và khi bạn xử lý bộ điều khiển, tất cả các lần xem và đầu ra của nó sẽ được giải phóng tự động.

UiViewControll sử dụng Mã hóa giá trị khóa để đặt các cửa hàng sử dụng các tham chiếu mạnh. Vì vậy, khi bạn giải quyết UIViewControll của bạn, chế độ xem trên cùng sẽ tự động được giải phóng, nhưng bạn cũng phải giải phóng tất cả các đầu ra của nó trong phương thức dealloc.

Trong bài đăng này từ Big Nerd Ranch , họ đề cập đến chủ đề này và cũng giải thích tại sao sử dụng tài liệu tham khảo mạnh mẽ trong IBOutlet không phải là một lựa chọn tốt (ngay cả khi được Apple khuyến nghị trong trường hợp này).


16
Nó giải thích nó vào năm 2009. Với ARC, điều này đã thay đổi đáng kể.
Dafydd Williams

1
:( liên kết Big Nerd Ranch đã chết nhưng tôi thực sự cần đọc nó. Có ai biết thêm chi tiết về bài đăng đó không, vì vậy tôi có thể tìm thấy nó?
Motti Shneor

@MottiShneor đừng lo lắng, đó không phải là vấn đề lớn vì liên kết đã có khoảng thời gian trước ARC và không còn phù hợp nữa.
Serge Grischyov

18

Một điều tôi muốn chỉ ra ở đây, và đó là, mặc dù những gì các kỹ sư của Apple đã tuyên bố trong video WWDC 2015 của riêng họ ở đây:

https://developer.apple.com/ideo/play/wwdc2015/407/

Apple liên tục thay đổi suy nghĩ của họ về vấn đề này, điều này cho chúng ta biết rằng không có câu trả lời đúng duy nhất cho câu hỏi này. Để chỉ ra rằng ngay cả các kỹ sư của Apple cũng bị chia rẽ về chủ đề này, hãy xem mã mẫu gần đây nhất của Apple và bạn sẽ thấy một số người sử dụng yếu và một số thì không.

Ví dụ về Apple Pay này sử dụng yếu: https://developer.apple.com/l Library / ios / samplecode / Entporium / Listings / Entporium_ ProducttTableViewContoder_swift.html # / // numplef / doc / uid / TP40016175-Eppor_

Ví dụ như trong ảnh này cũng như ví dụ về hình ảnh trong tranh: https://developer.apple.com/l Library / ios / samplecode / ARFoundationPiPPlayer / Listpl / ORFoundationPiPPlayer / Listings / UFoundationPiPPlayer_PlayerViewContoder_swift.html / //apple_Vf / doc / uid

Ví dụ về Trình liệt kê cũng như: https://developer.apple.com/l Library / ios / samplecode / Lister / Listings / Lister_ListCell_swift.html# // apple_Vf / doc / uid / TP40014701-Lister_ListCell_swift-DontLink

Ví dụ về Vị trí cốt lõi cũng như: https://developer.apple.com/l Library / ios / samplecode / PostLoc / Listings / Potloc_PotlocViewContoder_swift.html # / // bit_Vf / doc / uid / TP40016176-Partloc_Pot

Ví dụ về trình xem trước của trình điều khiển chế độ xem cũng như ví dụ: https://developer.apple.com/l Library / ios / samplecode / viewCont kiểmPreview / Listings / Propls_PreviewUsingDelegate

Ví dụ về HomeKit cũng như: https://developer.apple.com/l Library / ios / samplecode / HomeKitCatalog / Listings

Tất cả những thứ này được cập nhật đầy đủ cho iOS 9 và tất cả đều sử dụng các ổ cắm yếu. Từ đó chúng ta biết rằng A. Vấn đề không đơn giản như một số người đưa ra. B. Apple đã thay đổi suy nghĩ của họ nhiều lần và C. Bạn có thể sử dụng bất cứ điều gì khiến bạn hạnh phúc :)

Đặc biệt cảm ơn Paul Hudson (tác giả của www.hackingwithsift.com), người đã cho tôi làm rõ và tham khảo cho câu trả lời này.

Tôi hy vọng điều này làm rõ chủ đề tốt hơn một chút!

Bảo trọng.


Đôi khi tôi đã kiểm tra vấn đề này và không tìm thấy câu trả lời cụ thể nào. Vì liên kết trên cho thấy rằng cả hai đều ổn và nói chung đi với những gì tự động đề xuất Xcode.
subin272


6

Hãy nhận thức, IBOutletCollectionnên được @property (strong, nonatomic).


3
Tại sao không phải copylà nó NSArray?
siêu mã

5

Có vẻ như một cái gì đó đã thay đổi trong những năm qua và bây giờ Apple khuyên bạn nên sử dụng mạnh mẽ nói chung. Bằng chứng về phiên WWDC của họ là trong phiên 407 - Thực hiện Thiết kế giao diện người dùng trong Trình tạo giao diện và bắt đầu lúc 32:30. Ghi chú của tôi từ những gì anh ấy nói là (gần như, nếu không chính xác, trích dẫn anh ấy):

  • Các kết nối đầu ra nói chung phải mạnh mẽ đặc biệt nếu chúng ta kết nối một khung nhìn phụ hoặc ràng buộc không phải luôn được giữ lại bởi hệ thống phân cấp chế độ xem

  • kết nối ổ cắm yếu có thể cần thiết khi tạo chế độ xem tùy chỉnh có một số tham chiếu đến thứ gì đó được sao lưu trong hệ thống phân cấp chế độ xem và nói chung không nên sử dụng

Ở các phường khác, nó phải luôn luôn mạnh, miễn là một số chế độ xem tùy chỉnh của chúng tôi không tạo ra chu kỳ giữ lại với một số chế độ xem trong phân cấp chế độ xem

BIÊN TẬP :

Một số có thể đặt câu hỏi. Việc giữ nó với một tham chiếu mạnh không tạo ra chu trình giữ lại như trình điều khiển xem gốc và chế độ xem sở hữu giữ tham chiếu đến nó? Hay tại sao điều đó thay đổi xảy ra? Tôi nghĩ rằng câu trả lời là sớm hơn trong cuộc nói chuyện này khi họ mô tả cách các ngòi được tạo ra từ xib. Có một ngòi riêng được tạo cho một VC và cho chế độ xem. Tôi nghĩ rằng đây có thể là lý do tại sao họ thay đổi các khuyến nghị. Tuy nhiên, thật tuyệt khi nhận được lời giải thích sâu hơn từ Apple.


4

Tôi nghĩ rằng thông tin quan trọng nhất là: Các yếu tố trong xib sẽ tự động trong các lượt xem. Phỏng vấn là NSArray. NSArray sở hữu các yếu tố của nó. vv có con trỏ mạnh mẽ trên chúng. Vì vậy, trong hầu hết các trường hợp, bạn không muốn tạo một con trỏ mạnh khác (IBOutlet)

Và với ARC, bạn không cần phải làm gì trong viewDidUnload

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.