Sử dụng phân bổ init thay vì mới


344

Học Mục tiêu-C và đọc mã mẫu, tôi nhận thấy rằng các đối tượng thường được tạo bằng phương pháp này:

SomeObject *myObject = [[SomeObject alloc] init];

thay vì:

SomeObject *myObject = [SomeObject new];

Có một lý do cho điều này, như tôi đã đọc rằng chúng là tương đương?


12
Tôi thậm chí không biết điều đó là có thể! Tất cả các tài liệu tôi đã đọc giả vờ như từ khóa mới không tồn tại!
Jonathan

78
Trên thực tế "mới" không phải là một từ khóa trong Objective-C, nhưng NSObject thực hiện một phương thức lớp "mới" mà chỉ đơn giản gọi là "alloc" và "init".
Don McCaughey

3
Jonathan, đó chính xác là những gì gợi lên câu hỏi của tôi. Chúng có thể tương đương về chức năng nhưng [[alloc] init] rõ ràng là thành ngữ chủ đạo.
willc2

1
Tôi nghĩ Apple (từ những gì tôi có thể nhớ từ một trong những bài giảng về Stanford Stanford của họ) chỉ khuyến khích bạn sử dụng alloc init thay thế để bạn có thể hiểu quá trình xảy ra. Họ cũng không sử dụng nhiều mã mới trong mã mẫu của mình, vì vậy, phân bổ init dường như chỉ là một thói quen tốt chung mà Apple cố gắng phát huy.
PostCodeism

Câu trả lời:


290

Có rất nhiều lý do ở đây: http://macresearch.org/difference-b between-alloc -init-and-new

Một số được chọn là:

  • newkhông hỗ trợ khởi tạo tùy chỉnh (như initWithString)
  • alloc-init rõ ràng hơn new

Ý kiến ​​chung dường như là bạn nên sử dụng bất cứ thứ gì bạn thấy thoải mái.


8
Nếu lớp của bạn sử dụng -init làm trình khởi tạo được chỉ định, + new sẽ gọi nó. Điều Jeremy muốn nói đến là nếu bạn có một trình khởi tạo tùy chỉnh không -init. -initWithName:, ví dụ.
bbum

87
Không có gì ngăn cản bạn thực hiện +newWithString:, nếu bạn đã thực hiện -initWithString. Không phải là phổ biến mặc dù. Cá nhân tôi luôn sử dụng newkhi trình khởi tạo được chỉ định là init, chỉ ngắn và ngọt ngào.
PeyloW

11
Tôi biết đây là một chủ đề cũ, nhưng tôi chỉ muốn nhấn mạnh rằng bạn không nên thực hiện +newWithString:. Điều đó phá vỡ sự quan tâm. Vì khi bạn chỉ muốn sử dụng -init, không có lý do gì để không sử dụng +new.
Jonathan Sterling

7
@JonathanSterling: Apple có nhiều trường hợp họ dường như đang làm chính xác điều bạn khuyên chống lại; ví dụ [[NSString alloc] initWithFormat:...][NSString stringWithFormat:...]đều tương đương Bạn có nói rằng Apple đã vi phạm sự phân tách các mối quan tâm và không nên thực hiện theo cách này? (Lưu ý: Tôi không cố gắng hạ mình; tôi chỉ muốn biết thêm thông tin và biết nếu theo dõi sự dẫn dắt của Apple đôi khi là một ý tưởng tồi.)
Senseful 11/10/13

6
@Senseful Tôi tin rằng ngày trở lại trước ngày ARC (hoặc bộ sưu tập rác). Hai điều đó chúng ta không tương đương. stringWithFormat: sẽ trả về một chuỗi tự động phát hành trong khi alloc: init: sẽ cần được giải phóng bằng tay hoặc tự động phát hành, nếu không nó sẽ gây rò rỉ bộ nhớ.
msfeldstein

139

Câu hỏi rất cũ, nhưng tôi đã viết một số ví dụ chỉ để giải trí - có thể bạn sẽ thấy nó hữu ích;)

#import "InitAllocNewTest.h"

@implementation InitAllocNewTest

+(id)alloc{
    NSLog(@"Allocating...");
    return [super alloc];
}

-(id)init{
    NSLog(@"Initializing...");
    return [super init];
}

@end

Trong chức năng chính cả hai câu lệnh:

[[InitAllocNewTest alloc] init];

[InitAllocNewTest new];

dẫn đến cùng một đầu ra:

2013-03-06 16:45:44.125 XMLTest[18370:207] Allocating...
2013-03-06 16:45:44.128 XMLTest[18370:207] Initializing...

5
Câu trả lời tuyệt vời! Chỉ muốn thêm rằng sử dụng + mới là tuyệt vời cho các trường hợp bạn chỉ cần sử dụng -init và không có gì chuyên biệt hơn như -initWithS Something: ...
cgossain

Thật không may, ví dụ này không phải là bằng chứng cho thấy chúng giống hệt nhau. Nó chỉ là một ví dụ mà họ tạo ra kết quả tương tự.
Vince O'Sullivan

10
Thật không may, ví dụ này không phải là bằng chứng cho thấy chúng giống hệt nhau. Nó chỉ là một ví dụ nơi chúng xảy ra tạo ra kết quả tương tự. Mặt khác, điều này chứng tỏ rằng chúng khác nhau ... Lấy ví dụ ở trên, sửa đổi "initAllocNewTest.h" như sau: @interface initAllocNewTest: NSObject - (instancetype) __unav Available init; @end [[InitAllocNewTest alloc] init]sẽ không biên dịch trong khi [InitAllocNewTest new]không bị ảnh hưởng. (Xin lỗi vì thiếu ngắt dòng, v.v.)
Vince O'Sullivan

1
@ VinceO'Sullivan, điểm tốt. Điều này có nghĩa là nếu một người muốn làm cho init không khả dụng, như trong một lớp singleton, người ta cũng nên vô hiệu hóa mới. Tôi sẽ không liên lạc ở đây về việc singletons là tốt hay xấu.
dùng3099609

Tôi có một câu hỏi từ đoạn mã trên. tại sao "trở lại [siêu init];"? nói chung init là một phương thức trong đó các thành viên của lớp được khởi tạo, tại sao con trỏ thay đổi rất kỳ lạ!
Pruthvidhar Rao Nadunooru

52

+newtương đương với việc triển khai +alloc/-initcủa Apple NSObject. Rất khó có khả năng điều này sẽ thay đổi, nhưng tùy thuộc vào mức độ hoang tưởng của bạn, tài liệu của Apple +newxuất hiện để cho phép thay đổi triển khai (và phá vỡ tính tương đương) trong tương lai. Vì lý do này, vì "rõ ràng là tốt hơn ngầm định" và vì tính liên tục trong lịch sử, cộng đồng Objective-C thường tránh +new. Tuy nhiên, bạn có thể thường phát hiện ra các máy tính Java gần đây đến Objective-C bằng cách sử dụng chúng +new.


8
Mặc dù điều này thường đúng, nhưng cũng có một trại ủng hộ sự căng thẳng, trong trường hợp đó chỉ rõ ràng hơn khi có một mối quan tâm rõ ràng và hiện tại bảo đảm điều đó.
Peter De Weese

27
Tôi đã đánh giá thấp điều này bởi vì +newđã có từ thời NeXT. Nếu bất cứ điều gì +newlà một dấu hiệu của một người nào đó đã học objc từ lâu; Tôi thấy rất nhiều người trở nên mới mẻ với ngôn ngữ này, hoặc thậm chí đã viết nó trong nhiều năm nhưng rõ ràng sau sự bùng nổ của iOS, không biết ý +newnghĩa của nó là gì . Thứ hai, như đã +newrất cũ và từ thời NeXT, Apple sẽ khá điên rồ khi thay đổi nó theo cách phá vỡ mã cũ, đặc biệt là xem xét các cơ sở mã của riêng họ có thể bị vấy bẩn bởi nó.
asveikau

1
Tôi khá chắc chắn newthành ngữ đến từ Smalltalk. Nó cũng được sử dụng trong Ruby và cả Objective-C và Ruby đều lấy được rất nhiều cú pháp và quy ước của họ từ Smalltalk.
Brendon Roberto

3
Nó chỉ cho phép thay đổi trong phân bổ. Các tài liệu rõ ràng trạng thái -init sẽ được gọi. Vì vậy, nó phụ thuộc nhiều hơn vào việc bạn ghi đè phân bổ bao giờ.
Kendall Helmstetter Gelner

7
Tôi thực sự không thể tưởng tượng được tại sao một giải pháp mà bạn phải gõ THÊM sẽ được ưu tiên (cấp phát init), đặc biệt là trong một ngôn ngữ dài dòng như Objective-C trong đó việc lưu tổ hợp phím sẽ giúp bạn tiết kiệm thời gian. Những "nhà phát triển Java bướng bỉnh" đó chỉ đang sử dụng các kỹ năng phân tích của họ để dành ít thời gian hơn để viết mã thay vì gõ các ký tự phụ không cần thiết tạo ra kết quả chức năng tương tự.
DiscDev

9

Thường xuyên, bạn sẽ cần truyền các đối số initvà vì vậy bạn sẽ sử dụng một phương thức khác, chẳng hạn như [[SomeObject alloc] initWithString: @"Foo"]. Nếu bạn đã quen viết nó, bạn sẽ có thói quen làm theo cách này và do đó [[SomeObject alloc] init]có thể đến một cách tự nhiên hơn [SomeObject new].


7

Một câu trả lời ngắn gọn là:

  1. Cả hai đều giống nhau. Nhưng
  2. 'mới' chỉ hoạt động với trình khởi tạo 'init' cơ bản và sẽ không hoạt động với các trình khởi tạo khác (ví dụ: initWithString :).

3

Đối với một ghi chú bên lề, cá nhân tôi sử dụng [Foo new]nếu tôi muốn một cái gì đó trong init được thực hiện mà không sử dụng giá trị trả về của nó ở bất cứ đâu. Nếu bạn không sử dụng sự trở lại của [[Foo alloc] init]bất cứ nơi nào thì bạn sẽ nhận được một cảnh báo. Ít nhiều, tôi sử dụng [Foo new]cho kẹo mắt.


3

Tôi rất muộn với điều này nhưng tôi muốn đề cập rằng cái mới thực sự không an toàn trong thế giới Obj-C với Swift. Swift sẽ chỉ tạo một phương thức init mặc định nếu bạn không tạo bất kỳ trình khởi tạo nào khác. Gọi mới trên một lớp nhanh chóng với một trình khởi tạo tùy chỉnh sẽ gây ra sự cố. Nếu bạn sử dụng alloc / init thì trình biên dịch sẽ phàn nàn rằng init không tồn tại.


1

Nếu mới thực hiện công việc cho bạn, thì nó cũng sẽ làm cho mã của bạn nhỏ hơn một cách khiêm tốn. Nếu bạn muốn gọi[[SomeClass alloc] init] ở nhiều nơi khác nhau trong mã của mình, bạn sẽ tạo Điểm nóng trong triển khai mới - nghĩa là trong thời gian chạy objc - điều đó sẽ làm giảm số lần bỏ lỡ bộ nhớ cache của bạn.

Theo hiểu biết của tôi, nếu bạn cần sử dụng một trình khởi tạo tùy chỉnh, hãy sử dụng [[SomeClass alloc] initCustom] .

Nếu bạn không, sử dụng [SomeClass new].


1
Tôi sẽ tranh luận với "nếu bạn cần sử dụng trình khởi tạo tùy chỉnh, hãy sử dụng [[someClass alloc] initCustom]". Nếu bạn cần một trình khởi tạo tùy chỉnh mà không có bất kỳ tham số nào, đừng bao giờ làm điều gì đó như bạn đang đề xuất, chỉ cần ghi đè initchức năng mặc định và sử dụng nó [[SomeClass alloc] init];Nếu bạn cần tham số, vẫn không bao giờ làm điều gì đó như vậy, hãy làm [[SomeClass alloc] initWith:...];. Cuối cùng, nếu bạn ghi đè initchức năng bằng cách triển khai tùy chỉnh, bạn có thể gọi newkhi tạo một đối tượng và nó vẫn sẽ gọi việc initthực hiện tùy chỉnh .
Dirtdanee
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.