16

tôi cần phải tạo ra trong một ứng dụng iOS giả va_list để vượt qua một chức năng NSString initWithFormat:arguments:, đây là mã của tôi:giả va_list trong ARC

NSArray *fixedArguments = [[NSArray alloc] initWithArray:arguments]; 

NSRange range = NSMakeRange(0, [fixedArguments count]); 

va_list fakeArgList = (va_list)malloc(sizeof(NSString *) * [fixedArguments count]); 

__unsafe_unretained id *ptr = (__unsafe_unretained id *)fakeArgList; 

[fixedArguments getObjects:ptr range:range]; 

content = [[NSString alloc] initWithFormat:outputFormat 
              arguments:(va_list)fakeArgList]; 
free(fakeArgList); 

Trình biên dịch phàn nàn với thông báo này trên dòng cast:

error: cast of a non-Objective-C pointer type 'va_list' (aka 'char *') to '__unsafe_unretained id *' is disallowed with ARC 

chức năng getObjects:range: được định nghĩa như sau:

- (void)getObjects:(id __unsafe_unretained [])objects range:(NSRange)range; 

Tôi đã thử tất cả mọi thứ nhưng vẫn không thể loại bỏ lỗi này ...

Có giải pháp nào để tạo va_list giả với ARC được bật không? Tôi đang làm gì sai?

Trả lời

30

EDIT: Điều này không còn hoạt động. Như đã thấy trước trong câu trả lời ban đầu, ABI dường như đã thay đổi từ câu trả lời này

Phát xung quanh một chút và làm việc đó - Kiểm tra kỹ xem có bị rò rỉ hoặc bộ nhớ bị bỏ quên hay không.

NSArray *fixedArguments = [[NSArray alloc] initWithObjects: @"foo", @"bar", @"baz", nil]; 

    NSRange range = NSMakeRange(0, [fixedArguments count]); 

    NSMutableData* data = [NSMutableData dataWithLength: sizeof(id) * [fixedArguments count]];  

    [fixedArguments getObjects: (__unsafe_unretained id *)data.mutableBytes range:range]; 

    NSString* content = [[NSString alloc] initWithFormat: @"1: %@ 2: %@ 3: %@" arguments: data.mutableBytes]; 

    NSLog(@"%@", content); 

Tôi thích (ab) sử dụng NSMutableData như thế này để có được giữ lại/ngữ nghĩa phát hành trên một đoạn tùy ý bộ nhớ - Nó không nhất thiết phải liên quan đến các vấn đề trong tầm tay, nhưng đó là một mẹo nhỏ gọn gàng.

Như một lưu ý cho người đọc trong tương lai: Việc tạo một danh sách như thế này sẽ hoạt động với ABI hiện tại cho MacOS và iOS, nhưng nói chung nó không di động và không phải là cách tiếp cận tốt.

+0

Cảm ơn bạn rất nhiều ... Tôi đang xây dựng một cái nhìn bảng chọn mà đưa ra một plist chứa hàng loạt các từ điển, một định dạng in ấn, chúng ta hãy nói ' "(% @ -% @)% @"' và danh sách các phím điền vào chế độ xem bộ chọn với chuỗi được định dạng trích xuất dữ liệu từ tệp plist. Cách duy nhất tôi tìm thấy để sử dụng một định dạng in với một danh sách các đối số biến là giả mạo một va_list. Tôi biết nó không phải là lập trình rõ ràng nhưng tôi không thể đưa ra giải pháp tốt hơn, bất kỳ phương án hợp lệ nào thực sự được chào đón và tôi nghĩ tôi sẽ đăng một câu hỏi khác về vấn đề của tôi để tìm giải pháp sạch hơn. – Scakko

+0

Nếu bạn luôn làm việc với% @ và không bao giờ có bất kỳ tham số có kích thước nào khác, bạn chỉ có thể tìm kiếm các trường hợp% @ trong chuỗi và thay thế chúng bằng [mô tả đối tượng] cho từng tham số của bạn. Hiệu ứng tương tự, không có va_list giả. Nhưng điều đó sẽ không hoạt động đối với định dạng số hoặc bất kỳ thứ gì khác, trừ khi bạn muốn làm nhiều việc hơn. – ipmcc

+0

Tôi sẽ thử ngay hôm nay, cảm ơn bạn rất nhiều ... – Scakko

0

Có thể nếu bạn sẵn sàng thêm một chút nhanh chóng vào dự án của bạn!

Bit quan trọng là ánh xạ của NSArray đến [CVarArgType] là số tương đương nhanh cho va_list. Nếu bạn cố gắng truyền [AnyObject] đến [CVarArgType] bạn gây ra lỗi thời gian chạy, nhưng với map, chúng tôi có thể tạo danh sách cần thiết một cách rõ ràng.

Phần còn lại của mã là trình bao bọc mà tôi đã tạo để tôi có thể gọi nó từ obj-c. Bạn có thể tạo một trình bao bọc cho bất kỳ hàm obj-c nào mà bạn muốn gọi theo cách này.

@objc class StringFormat: NSObject { 
    class func format(key: String, args: [AnyObject]) -> String { 
     let locArgs: [CVarArgType] = args.map({ (arg: AnyObject) -> CVarArgType in 
      if let iArg = (arg is NSNumber ? arg.intValue : nil) { 
       return iArg 
      } 
      return arg as! CVarArgType 
     }); 
     return String(format: key, arguments: locArgs) 
    } 
}