2014-06-05 33 views
18

Ứng dụng của tôi sử dụng cấu trúc dữ liệu có thể inmutable hơi phức tạp được mã hóa trong tệp nhị phân. Tôi cần phải có quyền truy cập vào nó ở cấp độ byte, tránh bất kỳ sao chép. Thông thường, tôi sẽ sử dụng số học và con trỏ C hoặc C++, để truy cập và giải thích các giá trị byte thô. Tôi muốn làm như vậy với Swift.Con trỏ, Số học con trỏ và Dữ liệu thô trong Swift

Tôi đã phát hiện ra rằng các công việc sau:

class RawData { 
    var data: NSData! 

    init(rawData: NSData) { 
     data = rawData 
    } 

    func read<T>(byteLocation: Int) -> T { 
     let bytes = data.subdataWithRange(NSMakeRange(byteLocation, sizeof(T))).bytes 
     return UnsafePointer<T>(bytes).memory 
    } 

    func example_ReadAnIntAtByteLocation5() -> Int { 
     return read(5) as Int 
    } 
} 

Tuy nhiên, tôi không chắc chắn hiệu quả như thế nào nó được. Do data.subdataWithRangeNSMakeRange phân bổ các đối tượng mỗi khi tôi gọi cho chúng hay chúng chỉ là cú pháp để xử lý con trỏ?

Có cách nào tốt hơn để thực hiện việc này trong Swift không?

EDIT:

Tôi đã tạo ra một lớp Objective-C nhỏ mà chỉ gói gọn một chức năng để bù đắp một con trỏ bởi một số lượng nhất định của byte:

@implementation RawDataOffsetPointer 

inline void* offsetPointer(void* ptr, int bytes){ 
    return (char*)ptr + bytes; 
} 

@end 

Nếu tôi bao gồm lớp này trong tiêu đề bắc cầu, sau đó tôi có thể thay đổi phương thức read của mình thành

func read<T>(byteLocation: Int) -> T { 
    let ptr = offsetPointer(data.bytes, CInt(byteLocation)) 
    return UnsafePointer<T>(ptr).memory 
} 

sẽ không sao chép dữ liệu từ b của tôi uffer, hoặc phân bổ các đối tượng khác.

Tuy nhiên, sẽ vẫn tốt hơn khi thực hiện một số số học con trỏ từ Swift, nếu có thể.

+0

Ruột của tôi nói rằng nếu các subdata là bất biến, nhanh chóng sẽ không bận tâm để sao chép nó. –

+1

Chỉ cần vào thời điểm này - có cách nào trong Swift để đọc một dòng 'UInt8' từ một NSData một cách hoàn toàn an toàn không? Hoặc từ một tập tin thậm chí? Tôi đang sử dụng điệu nhảy NSData/subdata này trong tệp nhị phân dingus Tôi đang viết ngay bây giờ, tôi thực sự ghét nhìn vào điều đó "vô nghĩa" vô nghĩa nhưng có vẻ như cách duy nhất tôi có thể lấy byte thô ra khỏi tệp. – iluvcapra

Trả lời

16

Nếu bạn chỉ muốn làm điều đó trực tiếp, UnsafePointer<T> thể được thao tác số học:

let oldPointer = UnsafePointer<()> 
    let newPointer = oldPointer + 10 

Bạn cũng có thể đúc một con trỏ như vậy (UnsafePointer<()> tương đương với void *)

let castPointer = UnsafePointer<MyStruct>(oldPointer) 
+0

'let oldPointer = UnsafePointer <()>' cung cấp cho tôi một lỗi ''>' không phải là một postfix toán tử đơn nhất'. Tui bỏ lỡ điều gì vậy? – Robert

15

Tôi khuyên bạn nên xem xét NSInputStream, cho phép bạn đọc NSData dưới dạng một chuỗi byte (UInt8 trong Swift).

Đây là một mẫu nhỏ, tôi đặt lại với nhau ở sân chơi:

func generateRandomData(count:Int) -> NSData 
{ 
    var array = Array<UInt8>(count: count, repeatedValue: 0) 

    arc4random_buf(&array, UInt(count)) 
    return NSData(bytes: array, length: count) 
} 

let randomData = generateRandomData(256 * 1024) 

let stream = NSInputStream(data: randomData) 
stream.open() // IMPORTANT 

var readBuffer = Array<UInt8>(count: 16 * 1024, repeatedValue: 0) 

var totalBytesRead = 0 

while (totalBytesRead < randomData.length) 
{ 
    let numberOfBytesRead = stream.read(&readBuffer, maxLength: readBuffer.count) 

    // Do something with the data 

    totalBytesRead += numberOfBytesRead 
} 

Bạn có thể tạo một extension để đọc các kiểu dữ liệu như vậy:

extension NSInputStream 
{ 
    func readInt32() -> Int 
    { 
     var readBuffer = Array<UInt8>(count:sizeof(Int32), repeatedValue: 0) 

     var numberOfBytesRead = self.read(&readBuffer, maxLength: readBuffer.count) 

     return Int(readBuffer[0]) << 24 | 
      Int(readBuffer[1]) << 16 | 
      Int(readBuffer[2]) << 8 | 
      Int(readBuffer[3]) 
    } 
} 
+0

Đây chính xác là điều tôi đang tìm kiếm! –

11

Tôi muốn giới thiệu những cách đơn giản để sử dụng UnsafeArray.

let data = NSData(contentsOfFile: filename) 
let ptr = UnsafePointer<UInt8>(data.bytes) 
let bytes = UnsafeBufferPointer<UInt8>(start:ptr, count:data.length) 
+0

Khi tôi thử điều này, tôi nhận được một EXC_BAD_ACCESS khi nó được vào dòng byte. – Brian

+0

Với phiên bản Swift hiện tại, dòng cuối cùng phải là 'let bytes = UnsafeBufferPointer (bắt đầu: ptr, count: data.length)' –

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