Lỗi trình biên dịch: "phần tử khởi tạo không phải là hằng số thời gian biên dịch"


76

Khi biên dịch mã này, tôi gặp lỗi "phần tử khởi tạo không phải là hằng số thời gian biên dịch". Bất cứ ai có thể giải thích tại sao?

#import "PreferencesController.h"

@implementation PreferencesController

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}


NSImage* imageSegment = [[NSImage alloc] initWithContentsOfFile:@"/User/asd.jpg"];//error here

Câu trả lời:


105

Khi bạn xác định một biến bên ngoài phạm vi của một hàm, giá trị của biến đó thực sự được ghi vào tệp thực thi của bạn. Điều này có nghĩa là bạn chỉ có thể sử dụng một giá trị không đổi. Vì bạn không biết mọi thứ về môi trường thời gian chạy tại thời điểm biên dịch (các lớp nào có sẵn, cấu trúc của chúng là gì, v.v.), bạn không thể tạo các đối tượng c mục tiêu cho đến thời gian chạy, ngoại trừ các chuỗi không đổi, được cung cấp một cấu trúc và đảm bảo giữ nguyên như vậy. Những gì bạn nên làm là khởi tạo biến thành nil và sử dụng +initializeđể tạo hình ảnh của bạn. initializelà một phương thức lớp sẽ được gọi trước khi bất kỳ phương thức nào khác được gọi trên lớp của bạn.

Thí dụ:

NSImage *imageSegment = nil;
+ (void)initialize {
    if(!imageSegment)
        imageSegment = [[NSImage alloc] initWithContentsOfFile:@"/User/asd.jpg"];
}
- (id)init {
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}

4
Một tùy chọn khác là sử dụng một hàm với __attribute__ ((constructor)).

12
Tuy nhiên, một tùy chọn khác là chuyển loại tệp nguồn của bạn từ Objective-C sang Objective-C ++ (hoặc đổi tên nó từ .m thành .mm, cũng có tác dụng tương tự). Trong C ++, các trình khởi tạo như vậy không cần phải là các giá trị hằng số thời gian biên dịch và mã gốc sẽ hoạt động tốt.
imre

1
Có cách nào để khai báo một consttrong những thời trang như vậy? Tức là một biến chỉ có thể được đặt một lần và không bao giờ lặp lại?
defos1

Điều gì xảy ra nếu tôi muốn bao gồm một hằng số từ một số tệp. Có thể viết hàm khởi tạo nhiều lần không, tức là. nó có thể được bao gồm một số phần?
Vladimir Despotovic

@VladimirDespotovic Không, nó không thể bị chia cắt. Bạn có thể có một +initializephương thức cho các lớp khác nhau hoặc nó có thể gọi các hàm từ các tệp khác, nhưng bạn phải thực sự cẩn thận với những thứ như thế này. Tốt nhất nên tránh nếu có thể.
ughoavgfhw 13/09/17

23

Một biến toàn cục phải được khởi tạo thành một giá trị không đổi, như 4hoặc 0.0hoặc @"constant string"hoặc nil. Một phương thức khởi tạo đối tượng, chẳng hạn như init, không trả về một giá trị hằng số.

Nếu bạn muốn có một biến toàn cục, bạn nên khởi tạo nó nilvà sau đó trả về nó bằng phương thức class:

NSImage *segment = nil;

+ (NSImage *)imageSegment
{
    if (segment == nil) segment = [[NSImage alloc] initWithContentsOfFile:@"/user/asd.jpg"];
    return segment;
}

Tại sao bạn có thể có NSString tĩnh - sử dụng cú pháp @ "myString" nếu NSString là đối tượng?
Paul Brewczynski

4
@bluesm: Trình biên dịch Objective-C sử dụng một số thủ thuật để tạo một chuỗi được coi như một giá trị không đổi.
mipadi

11

Bởi vì bạn đang yêu cầu trình biên dịch khởi tạo một biến tĩnh với mã vốn là động.


6

Lý do là bạn đang xác định imageSegmentbên ngoài một hàm trong mã nguồn của bạn (biến tĩnh).

Trong những trường hợp như vậy, việc khởi tạo không thể bao gồm việc thực thi mã, chẳng hạn như gọi một hàm hoặc cấp phát một lớp. Bộ khởi tạo phải là một hằng số có giá trị được biết trước tại thời điểm biên dịch.

Sau đó, bạn có thể khởi tạo biến tĩnh bên trong initphương thức của mình (nếu bạn hoãn khai báo nó thành init).


1
Lớp lưu trữ tĩnh là vấn đề. Sự cố sẽ vẫn xảy ra ngay cả khi nó nằm trong một hàm (và được khai báo là tĩnh).
jww 20/09/13

@noloader: Tôi chưa bao giờ nói ngược lại ... :-) chỉ, trường hợp cụ thể của lưu trữ tĩnh trong trường hợp của OP là một biến toàn cục ... (trong ngoặc đơn, khái niệm - tất cả phụ thuộc vào người đọc là ai. tốt hơn là bắt đầu với khái niệm hoặc trường hợp cụ thể).
sergio

4

Bạn chắc chắn có thể # xác định macro như hình dưới đây. Trình biên dịch sẽ thay thế "IMAGE_SEGMENT" bằng giá trị của nó trước khi biên dịch. Mặc dù bạn sẽ đạt được việc xác định tra cứu toàn cục cho mảng của mình, nhưng nó không giống như một biến toàn cục. Khi macro được mở rộng, nó hoạt động giống như mã nội tuyến và do đó, một hình ảnh mới được tạo ra mỗi lần. Vì vậy, nếu bạn cẩn thận trong nơi bạn sử dụng macro, thì bạn sẽ đạt được hiệu quả trong việc tạo một biến toàn cục.

#define IMAGE_SEGMENT [[NSImage alloc] initWithContentsOfFile:@"/User/asd.jpg"];

Sau đó sử dụng nó ở những nơi bạn cần như hình bên dưới. Mỗi khi đoạn mã dưới đây được thực thi, một đối tượng mới được tạo với một con trỏ bộ nhớ mới.

imageSegment = IMAGE_SEGMENT
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.