2011-09-11 20 views
8

Tôi có 2 câu hỏi mới:Are NSStrings lưu trữ trên đống hoặc trên stack và là những gì một cách tốt để khởi tạo một

1) Xem xét dòng này:

NSString *myString = [[NSString alloc] initWithString: @"Value"]; 

Có hai điều tôi đã học được, nhưng tôi muốn xác nhận: Như tôi đã học, thông báo "phân bổ" chỉ ra rằng cá thể của NSString sẽ được lưu trữ trong bộ nhớ "heap". Tôi cũng hiểu rằng các biến nguyên thủy như "ký tự" được lưu trữ trong bộ nhớ "ngăn xếp".

Điều này có nghĩa rằng:

  • trường hợp của NSString được lưu trữ trong bộ nhớ heap;
  • VÀ đối tượng này có con trỏ iVar (khi phương thức initWithString được gọi) vào chuỗi "Giá trị" nguyên thủy "ký tự", nằm trong bộ nhớ ngăn xếp? Tính năng này hoạt động như thế nào?

Câu hỏi thứ hai là trực tiếp liên quan và gây cho tôi một tình trạng khó xử cá nhân (có lẽ vì tôi là thiếu một điểm): 2) Trong hai phương pháp bạn sẽ tham khảo ý kiến ​​và lý do tại sao ?:

NSString *myString = [[NSString alloc] initWithString: @"Value"]; 
NSString *myString = @"Value"; 

Nếu câu hỏi đầu tiên của tôi được xác nhận, cả hai cách tiếp cận nên "cuối cùng" trỏ tới ký tự được lưu trữ trong bộ nhớ ngăn xếp. Do đó, tôi không thực sự thấy mục đích của việc sử dụng tùy chọn đầu tiên và bị làm phiền với số lượng giữ lại.

Trả lời

13

Câu trả lời ngắn: Trong trường hợp này, cả hai dòng đều có cùng kết quả. Hãy gán trực tiếp chuỗi liên tục cho myString.

Còn câu trả lời:

Đúng là đối tượng Objective-C được cấp phát trên heap. Tuy nhiên, điều đó không đúng, các giá trị "nguyên thủy" luôn được lưu trữ trên ngăn xếp. Các biến cục bộ được lưu trữ trên stack, cho dù chúng nguyên thủy hay không. (Ví dụ, bạn có thể khai báo biến cục bộ là cấu trúc, không được coi là nguyên thủy.) Bạn có thể lưu trữ các giá trị nguyên thủy trên heap, nhưng cách duy nhất để truy cập chúng trong trường hợp đó là thông qua một con trỏ. Ví dụ:

int *someInt = malloc(sizeof(int)); // allocate a block of memory to hold an int 
*someInt = 42;      // store data in that memory 

Lý do mà chúng tôi luôn gợi ý sử dụng để đề cập đến đối tượng-C Mục tiêu là đối tượng luôn cấp phát trên heap. Nếu bạn muốn lưu trữ một cái gì đó trên ngăn xếp, trình biên dịch cần phải biết kích thước của nó. Trong Objective-C, kích thước của một đối tượng không được biết đến cho đến khi chương trình thực sự đang chạy.

Vì vậy, hãy quay lại chuỗi của bạn. Cố gắng thực hiện hai dòng sau:

NSString *foo = [[NSString alloc] initWithString:@"foo"]; 
NSString *bar = @"foo"; 

Nếu bạn phá vỡ sau khi dòng thứ hai, bạn sẽ thấy rằng foobar chứa cùng một địa chỉ; có nghĩa là, chúng trỏ đến cùng một đối tượng. Vì các đối tượng NSString là không thay đổi, nên việc tạo một đối tượng với một chuỗi không đổi chỉ trả về một con trỏ tới hằng số đó.

Tại sao thậm chí có một số -initWithString: trong NSString nếu đó là tất cả? NSString là một "nhóm lớp", đó là để nói rằng NSString là giao diện công cộng với một số lớp học nội bộ khác nhau. Nếu bạn vượt qua một NSString * đó không phải là một hằng số vào -initWithString:, đối tượng bạn lấy lại có thể là một thể hiện của một lớp khác với những gì bạn nhận được khi bạn sử dụng hằng số. Là một cụm lớp, NSString ẩn rất nhiều chi tiết triển khai từ bạn để bạn có được hiệu suất tốt cho các loại chuỗi khác nhau mà không phải lo lắng về cách hoạt động của tất cả các chuỗi.

4

NSString s rất thú vị vì gần đây chúng là loại đối tượng Mục tiêu-C duy nhất có thể được cung cấp dưới dạng chữ. Các đối tượng khối được thêm vào trong iOS 4 và OS X 10.6 là bổ sung gần đây khác mà tôi biết, nhưng chúng có các quy tắc riêng của chúng nên tôi đề cập đến điều đó chỉ để hoàn thành.

C nguyên thủy có thể được lưu trữ trên heap hoặc trên ngăn xếp. Ví dụ:

- (void)someMethod 
{ 
    int i = 3; // i is on the stack 
} 

- (void)someOtherMethod 
{ 
    int *i = (int *)malloc(sizeof(int)); // i now points to an 'int' on the heap 
} 

Hầu hết các đối tượng C chỉ có thể được lưu trữ trên heap. Bạn hoàn toàn đúng khi nói rằng alloc cung cấp bản sao mới của đối tượng trên heap và kết quả cuộc gọi myString của bạn sẽ có con trỏ đến NSString trên heap. Tuy nhiên, cú pháp @"" là viết tắt để tạo một đối tượng. @"Value" thực sự tạo ra một đối tượng theo nghĩa đen. Vì vậy, ví dụ: bạn có thể làm:

NSLog(@"%@", [@"Value" substringFromIndex:1]); 

Và đầu ra sẽ là 'alue'. Bạn có thể gửi thông báo substringFromIndex: tới @"Value" vì đó là một đối tượng theo nghĩa đen.

Chính xác những gì NSString thực hiện với initWithString: là một triển khai cụ thể nhưng bạn có thể chắc chắn rằng nó sẽ lấy một bản sao của điều được chỉ ra nếu cần. Nếu không, bạn sẽ thấy hành vi kỳ lạ nếu bạn đã thực hiện điều gì đó như thế này:

NSMutableString *mutableString = [NSMutableString stringWithString:@"String"]; 
NSString *immutableString = [NSString stringWithString:mutableString]; 

[mutableString appendString:@" + hat"]; 

// immutableString would now have mutated if it was simply 
// keeping a reference to the string passed in 

Do đó, bạn không cần phải lo lắng về bất cứ điều gì bạn vượt qua. Đó là công việc của NSString để giải quyết vấn đề đó.

Trong thực tế, NSString literals đối tượng không bao giờ thực sự hết hạn do đó, điểm là một chút tranh luận. Tuy nhiên, nếu bạn đã sử dụng initWithUTF8String: hoặc cái gì đó khác có chữ C và chữ C trên ngăn xếp, bạn vẫn không có gì phải lo lắng bởi vì NSString sẽ xử lý nó.

Để trả lời câu hỏi thứ hai của bạn, tôi ưu tiên phiên bản thứ hai với lý do ngắn hơn và do đó thể hiện rõ hơn những gì bạn định làm. Có một số lợi ích hiệu suất lý thuyết cho sau này - đặc biệt là nếu bạn sử dụng cùng một chữ ở nhiều nơi - nhưng chúng vô cùng đáng kể như không đáng để xem xét ngày nay.

Các vấn đề liên quan