NSData的原理与实现

NSData的原理与实现

顾名思义,NSData是数据集合的抽象。更准确的说,NSData是一个字节序列。
一般我们可以将文件的内容读取到NSData中,也可以将NSData写入文件。NSData可以表示一个文件。处理NSData可以生成字符串,可以生成一张图片等。
NSData的成员是

@interface CCData : NSObject
    void *_bytes;
    NSUInteger _length;
}


_bytes指向字节数组,_length表示字节数组的长度。

NSData有许多初始化方法。其中- (instancetype)initWithBytesNoCopy:(void*)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;最为重要,其他初始化方法调用该方法,完成初始化。


-(id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b
    _bytes=NULL;
    _length=0;
    if(length>0)
        if(bytes==NULL)
            [NSException raise:NSInvalidArgumentException format:@"bytes == NULL"];
        _bytes=bytes;
        _length=length;
    return self;
}

如果length为0,则_length为0,_bytes为NULL。如果length大于0,如果bytes为NULL,则抛出异常。否则_
bytes=bytes,_length=length,最后返回self。freeWhenDone参数无意义。


-(id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length
    return [self initWithBytesNoCopy:bytes length:length freeWhenDone:YES];
}

调用- (instancetype)initWithBytesNoCopy:(void*)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b完成任务。


-(id)initWithBytes:(const void *)bytes length:(NSUInteger)length
    void *buffer=NULL;
    NSUInteger bufferSize=0;
    if(length>0)
        if(bytes==NULL)
            [NSException raise:NSInvalidArgumentException format:@"bytes == NULL"];
        buffer=malloc(length);
        if(buffer==NULL)
            [NSException raise:NSMallocException format:@"malloc out of memory"];
        memcpy(buffer, bytes, length);
        bufferSize=length;
    self=[self initWithBytesNoCopy:buffer length:bufferSize];
    if(buffer)
        free(buffer);
    return self;


如果length为0,则_length=0,_bytes为NULL。否则判断bytes是否为NULL,如果是则抛出异常。如果不为NULL,则用malloc申请数组 malloc(sizeof(char)*length),并复制数组内容。然后调用- (instancetype)initWithBytesNoCopy:(void*)bytes length:(NSUInteger)length。


- (nullable instancetype)initWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr
//未实现
}

读取文件的内容到NSData中。参数path指定文件路径。readOptionsMask指定读取的方式。
errorPtr在读取出错时保存出错信息。readOptionsMask的可能值为
typedefNS_OPTIONS(NSUInteger, NSDataReadingOptions) {
NSDataReadingMappedIfSafe = 1UL << 0,
NSDataReadingUncached = 1UL << 1,
NSDataReadingMappedAlways NS_ENUM_AVAILABLE(10_7, 5_0) = 1UL << 3,
NSDataReadingMapped = NSDataReadingMappedIfSafe,
NSMappedRead = NSDataReadingMapped,
NSUncachedRead = NSDataReadingUncached
};
选项为NSDataReadingMappedIfSafe时文件读取使用mmap。
选项为NSDataReadingUncached时文件使用read读取。
选项为NSDataReadingMappedAlways时文件读取使用mmap。


-(id)initWithContentsOfFile:(NSString *)path
    void *bytes;
    NSUInteger length;
    if(readContentsOfFile(path, &bytes, &length)==NO)
        [self release];
        return nil;
    return [self initWithBytesNoCopy:bytes length:length];
}


static BOOL readContentsOfFile(NSString *path,void **buf,NSUInteger *len)
    const char *lpath;
    struct stat st;
    off_t fileLength;
    int fd;
    void *tmp=NULL;
    char fileBuf[4096];
    size_t c;
    if(path==nil)
        return NO;
    lpath=[path fileSystemRepresentation];
    if(stat(lpath, &st)==-1)
        return NO;
    if((st.st_mode&S_IFMT)!=S_IFREG)
        return NO;
    fileLength=st.st_size;
    fd=open(lpath, O_RDONLY);
    if(fd==-1)
        return NO;
    if(fileLength!=0)
        tmp=malloc(fileLength);
        if(tmp==NULL)
            return NO;
        if(read(fd, tmp, fileLength)!=fileLength)
            free(tmp);
            return NO;
    }else
        tmp=NULL;
        fileLength=0;
        while ((c=read(fd, fileBuf, 4096))>0) {
            if(tmp==NULL)
                tmp=malloc(c);
            }else
                tmp=realloc(tmp, fileLength+c);
            if(tmp==NULL)
                free(tmp);
                return NO;
            memcpy(tmp+fileLength, fileBuf, c);
            fileLength+=c;
    *buf=tmp;
    *len=fileLength;
    return YES;

读取文件内容到NSData中。


- (nullable instancetype)initWithContentsOfURL:(NSURL *)url options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr
//未实现
}

读取url指定的文件内容到NSData中。参数url指定文件,参数readOptionsMask指定读取方式。
参数errorPtr在出错时保存出错信息。


- (id)initWithContentsOfURL:(NSURL *)url
    return [self initWithContentsOfFile:[url path]];
}

读取url指定的文件的内容。


-(id)initWithData:(NSData *)data
    if(data==nil)
        [NSException raise:NSInvalidArgumentException format:@"data == nil"];
    return [self initWithBytes:[data bytes] length:[data length]];
}

根据NSData对象初始化。如果参数data为nil,则抛出异常。否则调用- (instancetype)initWithBytes:(nullable constvoid*)bytes length:(NSUInteger)length方法。

初始化方法对应了一些构造方法。

+ (NSData *)data
return [[[NSData alloc] initWithBytesNoCopy:NULL length:0]autorelease];
}

构造空的NSData对象。

+ (NSData *)dataWithBytesNoCopy:(void*)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b
return return [[[NSData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:b]autorelease];
}

根据字节数组构造NSData对象。不复制字节数组。

+ (NSData *)dataWithBytesNoCopy:(void*)bytes length:(NSUInteger)length
return return [[[NSData alloc] initWithBytesNoCopy:bytes length:length]autorelease];
}

根据字节数组构造NSData对象。不复制字节数组。

+ (NSData *)dataWithBytes:(nullable constvoid*)bytes length:(NSUInteger)length
return return [[[NSData alloc] initWithBytes:bytes length:length]autorelease];
}

根据字节数组构造NSData对象。


+ (NSData *)dataWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr
    return [[[NSData alloc] initWithContentsOfFile:path options:readOptionsMask error:errorPtr] autorelease];
}

读取文件内容构造NSData对象。


+ (NSData *)dataWithContentsOfURL:(NSURL *)url options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr
    return [[[NSData alloc] initWithContentsOfURL:url options:readOptionsMask error:errorPtr] autorelease];
}

读取url指定的文件构造NSData对象。


+ (NSData *)dataWithContentsOfFile:(NSString *)path
    return [[[NSData alloc] initWithContentsOfFile:path] autorelease];
}

读取文件内容构造NSData对象。


+ (NSData *)dataWithContentsOfURL:(NSURL *)url
    return [[[NSData alloc] initWithContentsOfURL:url] autorelease];
}

读取url指定的文件构造NSData对象。


+(NSData *)dataWithData:(NSData *)data
    return [[[NSData alloc] initWithData:data] autorelease];
}

根据NSData对象构造NSData对象。

以下是NSData常用的方法。


-(NSUInteger) length
    return _length;
}

返回字节数组的大小。


-(const void *)bytes
    return _bytes;
}


返回字节数组。


-(void)getBytes:(void *)buffer
    [self getBytes:buffer range:NSMakeRange(0, _length)];

获取字节数组。


-(void)getBytes:(void *)buffer length:(NSUInteger)length
    [self getBytes:buffer range:NSMakeRange(0, length)];
}

获取字节数组,获取的字节数组长度为length。


-(void)getBytes:(void *)buffer range:(NSRange)range
    if(buffer==NULL)
        [NSException raise:NSInvalidArgumentException format:@"buffer == NULL"];
    if(range.location>_length||range.length>_length-range.location)
        [NSException raise:NSRangeException format:@"range out of count"];
    memcpy(buffer, _bytes+range.location, range.length);
}

获取range区间内的字节数组。


-(BOOL)isEqualToData:(NSData *)other
    if(other==nil)
        [NSException raise:NSInvalidArgumentException format:@"other is nil"];
    if(self==other)
        return YES;
    if(_length!=[other length])
        return NO;
    if(memcmp(_bytes, [other bytes], _length)==0)
        return YES;
    return NO;
}

判断NSData是否相等。


-(NSData *)subdataWithRange:(NSRange)range
    if(range.location>_length||range.length>_length-range.location)
        [NSException raise:NSRangeException format:@"range out of count"];
    void *bytes=NULL;
    NSUInteger length=0;
    if(range.length>0)
        bytes=malloc(range.length);
        if(bytes==NULL)
            [NSException raise:NSMallocException format:@"malloc out of memory"];
        memcpy(bytes, _bytes+range.location, range.length);
        length=range.length;
    self=[self initWithBytesNoCopy:bytes length:length];
    if(bytes)
        free(bytes);
    return [self autorelease];
}

获取字节序列的子序列。


-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile
    const char *lpath=[path fileSystemRepresentation];
    int fd=open(lpath, O_WRONLY|O_CREAT|O_TRUNC,0664);
    if(fd==-1)
        return NO;
    if(write(fd, _bytes, _length)!=_length)
        return NO;
    close(fd);
    return YES;

写入文件。


- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically
    return [self writeToFile:[url path] atomically:atomically];
}

写入url指定的文件。

- (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)writeOptionsMask error:(NSError **)errorPtr
//未实现
}

写入文件。path指定文件路径,writeOptionsMask指定写入方式。errorPtr在出错时保存出错信息。writeOptionsMask的可能值是
typedefNS_OPTIONS(NSUInteger, NSDataWritingOptions) {
NSDataWritingAtomic = 1UL << 0,
NSDataWritingWithoutOverwriting NS_ENUM_AVAILABLE(10_8, 6_0) = 1UL << 1,
NSDataWritingFileProtectionNone NS_ENUM_AVAILABLE_IOS(4_0) = 0x10000000,
NSDataWritingFileProtectionComplete NS_ENUM_AVAILABLE_IOS(4_0) = 0x20000000,
NSDataWritingFileProtectionCompleteUnlessOpen NS_ENUM_AVAILABLE_IOS(5_0) = 0x30000000,
NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication NS_ENUM_AVAILABLE_IOS(5_0) = 0x40000000,
NSDataWritingFileProtectionMask NS_ENUM_AVAILABLE_IOS(4_0) = 0xf0000000,
NSAtomicWrite = NSDataWritingAtomic
};

- (BOOL)writeToURL:(NSURL *)url options:(NSDataWritingOptions)writeOptionsMask error:(NSError **)errorPtr
//未实现
}

写入url指定的文件。

- (NSRange)rangeOfData:(NSData *)dataToFind options:(NSDataSearchOptions)mask range:(NSRange)searchRange
}

搜索字节序列。dataToFind是搜索的字节序列,searchRange是搜索的区间,mask是搜索的模式。其可能值是
typedefNS_OPTIONS(NSUInteger, NSDataSearchOptions) {
NSDataSearchBackwards = 1UL << 0,
NSDataSearchAnchored = 1UL << 1
}
NSDataSearchBackwards是由后向前搜索。
NSDataSearchAnchored是由前向后搜索。

NSData是不可变的,不能添加删除。NSMutableData是Data的可变版本。NSMutableData继承自NSData。继承了NSData的都有方法。

NSMutableData的成员是
@interface CCMutableData : CCData
    //void *_bytes;
    //NSUInteger _length;
    NSUInteger _capacity;
    NSUInteger _grow;
@end


-(id)initWithCapacity:(NSUInteger)capacity
    if(capacity==0)
        capacity=1;
    _bytes=malloc(capacity);
    if(_bytes==NULL)
        [NSException raise:NSMallocException format:@"malloc out of memory"];
    _capacity=capacity;
    _length=0;
    _grow=capacity/2;
    if(_grow==0)
        _grow=1;
    return self;
}


根据容量初始化NSMutableData。


- (id)initWithBytes:(const void *)bytes length:(NSUInteger)length
    [self initWithCapacity:length];
    if(length>0)
        if(bytes==NULL)
            [NSException raise:NSInvalidArgumentException format:@"bytes is null"];
        memcpy(_bytes, bytes, length);
        _length=length;
    return self;


根据字节数组初始化NSMutableData。


-(id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b
    _bytes=malloc(1);
    _length=0;
    _capacity=1;
    _grow=1;
    if(_bytes==NULL)
        [NSException raise:NSMallocException format:@"malloc out of memory"];
    if(length>0)
        if(bytes==NULL)
            [NSException raise:NSInvalidArgumentException format:@"bytes == NULL"];
        free(bytes);
        _bytes=bytes;
        _capacity=length;
        _length=length;
        _grow=_capacity/2;
        if(_grow==0)
            _grow=1;
    return self;
}


根据字节数组初始化NSMutableData。



-(id)initWithLength:(NSUInteger)length
    [self initWithCapacity:length];
    memset(_bytes, 0, length);
    _length=length;
    return self;
}

根据长度初始化数据,数据都为0.


+ (NSMutableData *)dataWithCapacity:(NSUInteger)aNumItems
    return [[[NSMutableData alloc] initWithCapacity:aNumItems] autorelease];
}

根据容量构建数据

+ (NSMutableData *)dataWithLength:(NSUInteger)length
    return  [[NSMutableData alloc] initWithLength:length] autorelease];
}

根据长度构建数据。


-(void *)mutableBytes
    return _bytes;
}

返回字节序列


-(void)_grow:(NSUInteger)max
    if(max>_capacity)
        while (max>_capacity) {
            _capacity+=_grow;
            _grow=_capacity/2;
        _bytes=realloc(_bytes, _capacity);
}

增长容量。


-(void)appendBytes:(const void *)bytes length:(NSUInteger)length
    if(length>0)
        if(bytes==NULL)
            [NSException raise:NSInvalidArgumentException format:@"bytes == 0"];
        NSUInteger oldLength=_length;
        NSUInteger newLength=_length+length;
        if(newLength>_capacity)
            [self _grow:newLength];
        memcpy(_bytes+oldLength, bytes, length);
        _length=newLength;

添加字节序列。


-(void)appendData:(NSData *)other
    if(other==nil)
        [NSException raise:NSInvalidArgumentException format:@"other is nil"];
    [self appendBytes:[other bytes] length:[other length]];
}

添加数据


- (void)increaseLengthBy:(NSUInteger)extraLength
    NSUInteger oldLength=_length;
    NSUInteger newLength=_length+extraLength;
    [self _grow:newLength];
    memset(_bytes+oldLength, 0, extraLength);
}

增长数据。


- (void)replaceBytesInRange:(NSRange)range withBytes:(const void *)bytes
    if(range.location>_length)
        [NSException raise:NSRangeException format:@"location out of length"];
    if(range.length>0)
        if(bytes==NULL)
            [NSException raise:NSInvalidArgumentException format:@"bytes is null"];
        NSUInteger newLength=range.location+range.length;
        if(newLength>_length)
            [self _grow:newLength];
            _length=newLength;
        memcpy(_bytes+range.location,bytes,range.length);

替换区间内的字节


- (void)resetBytesInRange:(NSRange)range
    if(range.location>_length||range.length>_length-range.location)
        [NSException raise:NSRangeException format:@"range out of length"];
    memset(_bytes+range.location, 0, range.length);
}

重置区间内的数据


- (void)setData:(NSData *)data
    if(data==nil)
        [NSException raise:NSInvalidArgumentException format:@"data is nil"];
    [self _grow:[data length]];