29 апр. 2014 г.

Категории - теория и пример

Язык Objective-C обладает возможностью добавлять новые методы к уже существующим классам (т.е. расширение функциональности класса). При этом не требуется исходников класса и добавленные методы автоматически становятся доступными всем классам, унаследованным от изменяемого. Так можно добавить новый метод классу NSString (возьмем за пример) и этот метод автоматически добавится во все остальные классы.

Механизм, позволяющий расширять уже существующие классы (путем добавления новых методов, новые instance-переменные добавить таким образом нельзя), называется категорией.

Категория имеет свое имя, список методов и имя класса, который она расширяет. Описание категории имеет следующий вид:

#import "ClassName.h"
@interface ClassName (CategoryName)
  // объявление методов
@end

Реализация категории выглядит следующим образом:

#import "CategoryName.h"
@implementation ClassName (CategoryName)
  // реализация методов
@end

Ограничения при создании категорий:
- Невозможность добавления переменных;
- Возможная коллизия имен с самим классом, поэтому необходимо использовать оригинальные префиксы в наименовании своих методов.

Пример.
Расширим класс NSString, добавим метод который будет проверять является ли файл аудио-файлом. Создадим проект Foundation и добавим в проект новый класс-категорию NSStringAudioExtension (наследуемый от NSString):


Объявим в NSStringAudioExtension.h метод - (BOOL) isAudioFile:

#import <Foundation/Foundation.h>

@interface NSString (NSStringAudioExtension)

- (BOOL) isAudioFile;

@end

В NSStringAudioExtension.m реализуем метод:

#import "NSString+NSStringAudioExtension.h"

@implementation NSString (NSStringAudioExtension)

- (BOOL) isAudioFile {
    
    if ([self hasSuffix:@".mp3"] || [self hasSuffix:@".wav"] || [self hasSuffix:@".arm"]) {

        return YES;

    }
    
    return NO;
}

@end

Дальше возвращаемся в main.m (технически класс NSString должен подхватить его расширение):

#import <Foundation/Foundation.h>
#import "NSString+NSStringAudioExtension.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        NSString *audioFileStr = @"Scooter feat. Vicky Leandros - C'est Bleu.mp3";
        
        if ([audioFileStr isAudioFile]) {
            
            NSLog(@"Да, это аудио-файл");
            
        } else {
            
            NSLog(@"Нет, это не аудио-файл");
            
        }
        
    }
    return 0;
}

Результат выполнения приложения:


Заменим .mp3 на .mp4, результат выполнения:


27 апр. 2014 г.

View-based NSTableView на основе ячеек из NSView (Злосчастная кнопка). Продолжение

В предыдущей статье о View-based NSTableView на основе ячеек из NSView не было реализации по использованию кнопки на виде (замечание в комментарии читателя). Порывшись в поиске (вместе с несуразными лексическими словосочетаниями), хоть какого-то решения так и не было найдено. Было решено использовать делегат и дать право AppDelegate кликать на эту злосчастную кнопку. 
Все бы ничего, но как узнать индекс этой чертовой вьюхи, на которой находится эта кнопка? Подумав, поломав голову вместе с мозгами, решил по нажатию кнопки AppDelegate-м передавать ему эту вьюху. Ведь все они разные, т.к. под каждую строку выделена память, естественно и адреса их будут разные.
Создаем делегат - добавляем в проект новый protocol-файл. Объявим в нем метод который будет передавать право тыканья этой кнопки AppDelegate-у.

#import <Foundation/Foundation.h>

@protocol ButtonControllerProtocol <NSObject>

@required
- (void) viewButtonClicked: (NSButton *) clickedButton view: (NSView *) viewCell;

@end

Далее в ViewController.h добавим экшен на кнопку, подключим AppDelegate и укажем какой метод использовать при нажатии этой кнопки.

- (IBAction)buttonInfo:(id)sender {
    
    AppDelegate *myAppDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
    [myAppDelegate viewButtonClicked:sender view:[self view]];
    
}

В AppDelegate.h подключим созданный протокол и пропишем делегат:

#import "ButtonControllerProtocol.h"

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDelegate, NSTableViewDataSource, ButtonControllerProtocol> {

Реализуем протокольный метод:

- (void)viewButtonClicked: (NSButton *) clickedButton view: (NSView *) viewCell {
    NSLog (@"click");
}

Все бы хорошо, метод обрабатывается, выдает определенный результат, а дальше-то что? А дальше нам якобы нужно как-то менять данные на вьюхе. Вот здесь-то и пришлось помучатся. Хотя все оказалось немного проще. Мы помним что окно как главный вид, состоит из подвидов разной сложности, т.е. проще говоря на нашей вьюхе-подвиде тоже есть подвиды (subviews) - nsimageview, nstextfield и т.д. Остается лишь получить к ним доступ. Получим подвиды на вьхе-строке:


Загоним эти объекты в массив и потом создадим указатели на эти объекты и будем обращаться к непосредственно новым объектам-подвидам:

- (void)viewButtonClicked: (NSButton *) clickedButton view: (NSView *) viewCell {

    // Получаем строку-вид в нашей таблице
    NSView *clickedView = [clickedButton superview];
    
    // Получаем список подвидов в строке-виде
    NSArray *viewObjectsArray = [NSArray arrayWithArray:[clickedView subviews]];
    
    // Коннектимся к подвиду и задаем значение
    NSImageView *viewObjectImage = [viewObjectsArray objectAtIndex:0];
    [viewObjectImage setImage:[NSImage imageNamed:@"Slice 1"]];
    
    NSTextField *viewObjectFirstTextField = [viewObjectsArray objectAtIndex:1];
    [viewObjectFirstTextField setStringValue:@"Slice 1"];
    
}

Вот такое вот нетривиальное, не претендующее ни на что, решение. Если есть более человеческое решение - прошу указать в комментариях.

20 апр. 2014 г.

Кастомизация строки / Атрибуты строки

Кастомизация строки состоит в том чтобы изменить в строке в определенном слове или символе цвет, шрифт, высоту и т.д. За все это отвечают классы NSAttributedString и NSMutableAttributedString. Например поменяем шрифт для первых пяти символов:
NSMutableAttributedString *seatText = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"Seats\n%d", seats]];
[seatText addAttribute:NSFontAttributeName
                 value:[UIFont boldSystemFontOfSize:25.0F]
                 range:NSMakeRange(0,5)];
return seatText;

Для большего ознакомления и понимания использования классов можно воспользоваться техдокументацией от Apple.

14 апр. 2014 г.

View-based NSTableView на основе ячеек из NSView

Суть построения View-based таблицы заключается в том, чтобы вместо обычных строк, состоящих из NSImageView и NSTextField (как показано в предыдущем примере), создать кастомную (свою модель компонентов в строке) строку. И здесь как всегда на помощь приходит NSView, а точнее NSViewController - класс.

Создаем новый проект. Назовем его ViewBasedNSTableViewWithNSView (Вы можете его назвать по своему, суть этого не меняется). Как и в предыдущем примере добавим таблицу, сделаем строку View-based (прочтите предыдущий пример, здесь в этом посте я упущу эти тонкости). Далее добавим новый класс наследуемый от NSViewController c xib-формой. Добавим компоненты интерфейса на  xib-форму. Я добавил два Label, кнопку с изображением ImageCell и ProgressBar. Т.е. можно накидать компоненты по своему желанию и усмотрению, а также целевому типу. Должно получится что-то из этого:


В ViewController.h сделаем оутлеты к нашим компонентам и к самой вьюхе и парочку методов.


#import <Cocoa/Cocoa.h>

@interface ViewController : NSViewController {
    
    IBOutlet NSImageView *iconView;
    IBOutlet NSTextField *messageLabelOne;
    IBOutlet NSTextField *messageLabelTwo;
    NSView *_mainView;
    
}

- (void) setStringValueForFirstTextFields: (NSString *) stringValue;
- (void) setStringValueForSecondTextFields: (NSString *) stringValue;
- (void) setIconForRowOnView: (NSImage *) iconContent;

@end


В ViewController.m распишем код для наших методов, также по умолчанию зададим текст для лэйблов:


#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Initialization code here.
    }
    return self;
}

- (id)init {
    
    [self.view addSubview: _mainView];
            
    [messageLabelOne setStringValue:@"Message One"];
    [messageLabelTwo setStringValue:@"Message Two"];
    
    return self;
}

- (void) setStringValueForFirstTextFields: (NSString *) stringValue {
    
    [messageLabelOne setStringValue:stringValue];
    
}

- (void) setStringValueForSecondTextFields: (NSString *) stringValue {
    
    [messageLabelTwo setStringValue:stringValue];
    
}

- (void) setIconForRowOnView: (NSImage *) iconContent {
    
    [iconView setImage:iconContent];
    
}

@end


Подключим хидер контроллера-модели ячейки в AppDelegate.h. Объявим массив в котором будем хранить вьюхи-объекты, свяжем оутлет переменной NSTableView с таблицей на форме, добавим объект-контроллер (по которому будем обращаться к индексу массива объектов и получать наш объект для текущего индекса строки. Помните?, я уже упоминал что строки в таблицу добавляются строчка-за-строчкой.).


#import <Cocoa/Cocoa.h>
#import "ViewController.h"

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDelegate, NSTableViewDataSource> {
    
    NSMutableArray *objArray;
    ViewController *vController;
}

@property (assign) IBOutlet NSWindow *window;
@property (weak) IBOutlet NSTableView *tableView;

@end


Дальше будем создавать объекты, задавать им соответствующие иконки и значения лэйблам, помещать в массив и дальше дело техники делегированных методов NSTableView.


#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
}

- (void)awakeFromNib {
    
    objArray = [[NSMutableArray alloc] init];
    
    ViewController *oneController = [[ViewController alloc] init];
    ViewController *twoController = [[ViewController alloc] init];
    ViewController *treeController = [[ViewController alloc] init];
    ViewController *fourController = [[ViewController alloc] init];
    ViewController *fiveController = [[ViewController alloc] init];
    ViewController *sixController = [[ViewController alloc] init];
    ViewController *sevenController = [[ViewController alloc] init];
    
    [oneController setStringValueForFirstTextFields:@"Hello World"];
    [oneController setStringValueForSecondTextFields:@"Hello To You"];
    [oneController setIconForRowOnView:[NSImage imageNamed:@"layers.icns"]];
    
    [twoController setStringValueForFirstTextFields:@"Hello World"];
    [twoController setStringValueForSecondTextFields:@"Hello To You"];
    [twoController setIconForRowOnView:[NSImage imageNamed:@"Slice 1.icns"]];
    
    [objArray addObject:oneController];
    [objArray addObject:twoController];
    [objArray addObject:treeController];
    [objArray addObject:fourController];
    [objArray addObject:fiveController];
    [objArray addObject:sixController];
    [objArray addObject:sevenController];
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    
    return [objArray count];
}

- (id)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    
    vController = [objArray objectAtIndex:row];
    return [vController view];
    
}

@end


Должна получится вот такая каша:
В принципе все. Самое главное - это понять как работает делегирование в таблице и сама таблица. Все остальное дело техники. При моей занятости (работа, семья) на понимание всей этой каши (по работе с таблицами в целом) ушло месяц-полтора. Если посчитать время уделенное на разборку всего этого, то общая затраченная сумма при интенсивном изучении заняла бы всего-то меньше недели.

Продолжение

9 апр. 2014 г.

Класс NSNumber - примеры

Этот класс является одним из самых странных дополнений в языке Objective-C. Для обработки числовых типов достаточно внутренних функций и методов. Единственная, как бы, цель класса NSNumber это обработка объектов NSArray, который хранит в себе только объекты.

Инициализация объекта NSNumber

Суть такова, что основные методы инициализации начинаются одинаково, но отличаются лишь конечным типом числа, т.е. что имеется в виду:

numberWith<Unsigned><Type>

Исходя из этого полное имя метода, для определенного типа числа, выглядит так:

numberWithBool
numberWithChar
numberWithDouble
numberWithFloat
numberWithInt
numberWithInteger
numberWithLong
numberWithLongLong
numberWithShort
numberWithUnsignedChar
numberWithUnsignedInt
numberWithUnsignedInteger
numberWithUnsignedLong
numberWithUnsignedLongLong
numberWithUnsignedShort


Например:

NSNumber *myFloat;
myFloat = [NSNumber numberWithFloat: 10.09];

Возвращение и приведение к другому типу числа из переменной-объекта класса

boolValue
charValue
decimalValue
doubleValue
floatValue
intValue
integerValue
longLongValue
longValue
shortValue
unsignedCharValue
unsignedIntegerValue
unsignedIntValue
unsignedLongLongValue
unsignedLongValue
unsignedShortValue


Например: 

NSNumber *myFloat; 
 
float floatvalue;
myFloat = [NSNumber numberWithDouble: 10.09];
floatvalue = [myFloat floatValue];
NSLog (@"Value = %f", floatvalue);

Сравнение числовых объектов 

Для сравнения значения, хранящихся в объектах необходимо использовать isEqualToNumber метод. isEqualToNumber возвращает логическое значение в зависимости от содержания двух объектов. Например:

NSNumber *myFloat1;
NSNumber *myFloat2;

myFloat1 = [NSNumber numberWithDouble: 10.09];
myFloat2 = [NSNumber numberWithDouble: 10.08];

if ([myFloat1 isEqualToNumber: myFloat2])
        NSLog (@"Numbers are equal");
else
        NSLog (@"Numbers are not equal");

Или просто сравнить эти объекты методом compare:

NSNumber *myFloat1;
NSNumber *myFloat2;

myFloat1 = [NSNumber numberWithDouble: 10.09]; 
myFloat2 = [NSNumber numberWithDouble: 10.08]; 
NSComparisonResult result; 
    
result = [myFloat1 compare: myFloat2];

if (result == NSOrderedSame)
        NSLog(@"Numbers are equal");
else if (result == NSOrderedAscending)
        NSLog(@"Float1 is less than Float2");
else if (result == NSOrderedDescending)
        NSLog(@"Float1 is greater than Float2");

Конвертация числа в строку

NSNumber *myFloat;
NSString *myString;
      
myFloat = [NSNumber numberWithDouble: 10.09];
myString = [myFloat stringValue];
NSLog (@"Number as string is %@", myString);

6 апр. 2014 г.

Определение объема HDD и оставшегося свободного места

Определение объема HDD и оставшегося свободного места

//get startup disk total size
+(NSNumber *)getDiskTotalSize
{
    NSDictionary *attr = [[NSFileManager defaultManager] attributesOfFileSystemForPath:@"/" error:nil];
    
    return [attr objectForKey:NSFileSystemSize];
}

//get startup disk's free size
+(NSNumber *)getDiskAvailableSize
{
    NSDictionary *attr = [[NSFileManager defaultManager] attributesOfFileSystemForPath:@"/" error:nil];
    
    return [attr objectForKey:NSFileSystemFreeSize];
}

Определение домашней папки пользователя

Определение домашней папки пользователя

//get user home
+(NSString *)getUserHome
{
    const char *home = getpwuid(getuid())->pw_dir;
    NSString *path = [[NSFileManager defaultManager]
                      stringWithFileSystemRepresentation:home
                      length:strlen(home)];
    NSString *realHomeDirectory = [[NSURL fileURLWithPath:path isDirectory:YES] path];
    
    return realHomeDirectory;
}

Перекодировка строки в UTF8 и обратно

Код перекодировки строки в UTF8 и обратно

//Encode NSString to UTF8
+ (NSString *)encode2UTF8:(NSString *)originalStr
{
    NSString *encodedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,(CFStringRef)originalStr,NULL,NULL,kCFStringEncodingUTF8));
    
    return encodedString;
}

//Decode UTF8 NSString
+ (NSString *)decodeFromUTF8:(NSString *)utf8Str
{
    return [utf8Str stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}

3 апр. 2014 г.

Системные директории в Objective-C

Директория /tmp

Большинство операционных систем имеют стандартный каталог предоставленный специально для целей временного хранения файлов. Другие системы также обеспечивают другую временную папку для каждого пользователя. Точное место отличается в зависимости от платформы. Самым безопасным будет использование NSTemporaryDirectory(), чтобы определить соответствующий каталог. Эта функция возвращает временный каталог для текущего пользователя в виде объекта NSString.
Следующий фрагмент кода определяет и затем отображает временный каталог, используя NSTemporaryDirectory() функцию:

NSString *tempdir;
tempdir = NSTemporaryDirectory();
NSLog (@"Temp Dir = %@", tempdir);

Определение домашней папки пользователя

Домашний каталог текущего пользователя может быть определен с помощью NSHomeDirectory() функции. Эта функция не принимает аргументов и возвращает объект NSString, содержащий путь к домашней директории пользователя:

NSString *homedir;
homedir = NSHomeDirectory();
NSLog (@"Home directory of current user is %@", homedir);

Определение домашней папки конкретно заданного пользователя

Домашний каталог любого пользователя в системе может быть получен с помощью NSHomeDirectoryForUser() функции. Эта функция принимает в качестве единственного аргумента объекта NSString, содержащий имя пользователя и возвращает другой объект NSString, содержащий соответствующий домашний каталог:

NSString *homedir;
NSString *username = @"Paul";
homedir = NSHomeDirectoryForUser(username);
NSLog (@"Home directory of user %@ is %@", username, homedir);

Если заданного пользователя не существует, запрос вернет nil.

Извлечение имени файла

Как уже говорилось ранее, путь может состоять из каталога, в котором находится файл, за которым следует имя файла. Общим требованием при работе с файлами при программировании на любом языке является извлечение только имя файла из пути. Это может быть легко достигнуто с помощью lastPathComponent метод:

NSString *samplepath = @"/Users/demo/objc/sample.m";

NSString *filename;
filename = [samplepath lastPathComponent];
NSLog (@"lastPathComponent = %@", filename);

Извлечение расширения файла

Имена файлов после '.' имеют некое расширение. Например, расширение файла myfile.txt является TXT. Расширение файла может быть извлечено из пути с использованием метода pathExtension:

NSString *samplepath = @"/Users/demo/objc/sample.m";
NSString *pathext;
pathext = [samplepath pathExtension];
NSLog (@"pathExtension = %@", pathext);

Стандартизация пути

Удивительно, как быстро путь может осложниться, особенно когда пользователь перемещаться по файловой системе. Пакет NSUtilities предоставляет метод stringByStandardizingPath. В следующем коде Мы будем использовать этот метод, чтобы сделать упрощение пути:

Например: ~demo/objc/code/../header/./../includes/mycode.h

NSString *samplepath = @"/Users/demo/objc/code/../header/./../includes/mycode.h";
NSString *cleanpath;
cleanpath = [samplepath stringByStandardizingPath];
NSLog (@"Standardized path = %@", cleanpath);

В результате выполнения получим правильный путь: /Users/demo/objc/includes/mycode.h

Разложение пути на его составляющие директории

Метод pathComponents извлекает различные части пути, которые составляют полный путь и возвращает их в NSArray:

NSString *samplepath = @"/Users/demo/objc/code/includes/mycode.h";
NSString *component;
NSArray *pathcomponents;
pathcomponents = [samplepath pathComponents];
for (component in pathcomponents)
        NSLog (@"component = %@", component);

В результате получим массив-список директорий из которых состоит путь:

component = /
component = Users
component = demo
component = objc
component = code
component = includes
component = mycode.h


2 апр. 2014 г.

Обработка файлов с помощью класса NSFileHandle

Класс NSFileHandle предоставляет широкий спектр методов, предназначенных для обеспечения более продвинутого механизма работы с файлами. В дополнение к файлам, этот класс также может быть использован для работы с устройствами и сетевыми сокетами. В следующих пунктах Мы рассмотрим некоторые из наиболее распространенных применений данного класса.

Инициализация NSFileHandle класса

Объект NSFileHandle создется при открытии файла для чтения, записи или обновления (чтение и запись). Это достигается с помощью fileHandleForReadingAtPath, fileHandleForWritingAtPath и методом fileHandleForUpdatingAtPath соответственно. Открыв файл, он впоследствии должен быть закрыт, когда Мы закончили работать с ним, используя метод CloseFile. Если попытка открыть файл не удается, к примеру, потому что делается попытка открыть несуществующий файл для чтения, эти методы возвращают ноль.

Например, следующий фрагмент кода открывает файл для чтения и записи, а затем закрывает его, фактически ничего не делая с файлом:

NSFileHandle *file;
file = [NSFileHandle fileHandleForWritingAtPath: @"/tmp/myfile.txt"];
if (file == nil)
        NSLog(@"Failed to open file");

[file closeFile];

NSFileHandle и позиция курсора

NSFileHandle объекты поддерживают указатель на текущей позиции в файле. Это упоминается как курсор. Когда файл открывается в первый раз курсор устанавливается в 0 (начало файла). Это означает, что любые операции чтения или записи которые Мы совершаем с помощью методов NSFileHandle начнутся с нулевой позиции курсора в файле. Для выполнения операций в разных местах в файле (например, для добавления данных в конец файла) в первую очередь необходимо стремиться к необходимости перемещения курсора в конец файла. Например, чтобы переместить текущую позицию курсора в конец файла, используйте метод seekToEndOfFile. Кроме того, seekToFileOffset позволяет указать точное местоположение в файле, к которое должен расположиться курсор. Наконец, текущее положение курсора может быть идентифицировано с помощью метода offsetInFile.

В следующем примере открывается файл для чтения, а затем выполняет ряд вызовов метода для перемещения курсора в различное местоположение файла:

NSFileHandle *file;
file = [NSFileHandle fileHandleForUpdatingAtPath: @"/tmp/myfile.txt"];
if (file == nil)
        NSLog(@"Failed to open file");

NSLog (@"Offset = %llu", [file offsetInFile]);
[file seekToEndOfFile];
NSLog (@"Offset = %llu", [file offsetInFile]);
[file seekToFileOffset: 30];
NSLog (@"Offset = %llu", [file offsetInFile]);
[file closeFile];

Курсор в файлах один из ключевых аспектов работы с файлами с помощью класса NSFileHandle, и нужно много практиковаться чтобы хорошо этот принцып понимать. Не зная позиции курсора - неизвестно откуда считаются или куда запишутся Ваши данные.

Чтение данных из файла

После того, как файл был открыт и присвоен дескриптор файла, содержимое этого файла может быть считано из текущего положения курсора. Метод readDataOfLength читает указанное число байт данных из файла, начиная с текущей позиции курсора. Например, следующий код считывает 5 байт данных из 10-й позиции в файле. Считанные данные будут хранится в объекте NSData:

NSFileHandle *file;
NSData *databuffer;

file = [NSFileHandle fileHandleForReadingAtPath: @"/tmp/myfile.txt"];
if (file == nil)
        NSLog(@"Failed to open file");

[file seekToFileOffset: 10];
databuffer = [file readDataOfLength: 5];
[file closeFile];

Альтернатива - метод readDataToEndOfFile будет читать все данные в файле, начиная с текущего полжения курсора аж до конца файла.

Запись в файл

Метод writeData записывает данные, содержащиеся в объекте NSData в файл, начиная с местоположения курсора. Обратите внимание, метод не дописивает данные, а перезаписывает все существующие данные в файле в соответствующем месте.

Чтобы увидеть это в действии, создадим файл с помощью текстового редактора с именем quickfox.txt, наберем следующий текст и сохраним его в каталоге /tmp:

"Быстрый коричневый кот перепрыгнул через ленивую собаку"

Далее, напишем код программы, которая открывает файл для обновления, перенесем курсор в позицию 10, а затем запишем некоторые данные в этом месте:


#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    @autoreleasepool {

        NSFileHandle *file;
        NSMutableData *data;

        const char *bytestring = "коричневый кот";
        data = [NSMutableData dataWithBytes:bytestring length:strlen(bytestring)];

        file = [NSFileHandle fileHandleForUpdatingAtPath: @"/tmp/quickfox.txt"];
        if (file == nil)
                NSLog(@"Failed to open file");

        [file seekToFileOffset: 10];
        [file writeData: data];
        [file closeFile];
    }
    return 0;
}

Когда наша программа выполнится, содержимое quickfox.txt будет изменен на:
"Быстрый коричневый кот перепрыгнул через ленивую собаку". 

Усечение (обрезание) файла

Файл может быть усечен с указанным смещением курсора, используя метод truncateFileAtOffset. Чтобы удалить все содержимое файла необходимо указать положение курсора 0 при вызове этого метода:

NSFileHandle *file;
file = [NSFileHandle fileHandleForUpdatingAtPath: @"/tmp/quickfox.txt"];
if (file == nil)
    NSLog(@"Failed to open file");

[file truncateFileAtOffset: 0];
[file closeFile];

NSFileManager - работа с каталогами и файлами (примеры). Часть 2

Продолжение первой части примеров по NSFileManager.

Копирование файла

Копирование файлов может быть достигнуто с помощью метода copyItemAtPath. Как и в методе перемещения, этот метод принимает в качестве аргументов путь-источник и путь назначения, а также дополнительный объект NSError. Успех операции в возвращенном логическое значении:

NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
if ([filemgr copyItemAtPath: @"/tmp/myfile.txt" toPath: @"/Users/demo/newfile.txt" error: NULL]  == YES)
        NSLog (@"Скопировано");
else
        NSLog (@"Ошибка копирования");

Удаление файла

Метод removeItemAtPath удаляет указанный файл из файловой системы. Метод принимает в качестве аргументов путь к файлу, и дополнительный объект NSError. Успех операции, как обычно - в виде возвращения логического завершения операции ДА или НЕТ:

NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
if ([filemgr removeItemAtPath: @"/tmp/myfile.txt" error: NULL]  == YES)
        NSLog (@"Файл удален");
else
        NSLog (@"Ошибка удаления файла");

Создание ссылки на файл


Ссылка на конкретный файл может быть создана с использованием метода createSymbolicLinkAtPath. Этот метод принимает в качестве аргументов путь к ссылке, и путь к файлу, для которого эта ссылка создается и дополнительный объект NSError. Например, следующий код создает ссылку /tmpUsers/demo/myfile21.txt, которая связывает с уже существующим файлом /tmp/myfile.txt:
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
if ([filemgr createSymbolicLinkAtPath: @"/tmp/myfile2.txt"
                withDestinationPath: @"/tmp/myfile.txt" error: NULL] == YES)
        NSLog (@"Ссылка создана успешно");
else
        NSLog (@"Ошибка");NSFileManager *filemgr;

filemgr = [NSFileManager defaultManager];
if ([filemgr createSymbolicLinkAtPath: @"/Users/demo/file1.txt" 
                withDestinationPath: @"/tmp/myfile.txt" error: NULL] == YES)
        NSLog (@"Ссылка создана успешно");
else
        NSLog (@"Ошибка");
Чтение и запись файлов

Класс NSFileManager содержат в себе некоторые основные методы для чтения и записи файла. Но эти возможности несколько ограничены по сравнению с вариантами, предусмотренными в классе NSFileHandle (в будущей статье об этом расскажу), но тем не менее полезны и эти методы.
Во-первых, содержимое файла может быть прочитано и храниться в объекте NSData за счет использования метода contentsAtPath:
NSFileManager *filemgr;
NSData *databuffer;
filemgr = [NSFileManager defaultManager];
databuffer = [filemgr contentsAtPath: @"/tmp/myfile.txt" ];
Дальше содержимое файла в объекте NSData можно обработать, и новые данные могут впоследствии быть записаны в новый файл с помощью метода createFileAtPath:

databuffer = [filemgr contentsAtPath: @"/tmp/myfile.txt" ];
[filemgr createFileAtPath: @"/tmp/newfile.txt" contents: databuffer attributes: nil];

В приведенном выше примере Мы скопировали содержимое из существующего файла в новый файл. К сожалению это не дает нам никакого контроля над тем, сколько данных будет читаться или записываться и не позволяет добавлять данные в конец существующего файла. Если файл /tmp/newfile.txt в приведенном выше примере уже существовал,то и любые в нем содержащиеся данные, будут перезаписаны содержимым исходного файла. Нужен некоторый более гибкий механизм. Это предусмотрено в классе NSFileHandle, ок отором я расскажу немножко позже.

NSFileManager - работа с каталогами и файлами (примеры). Часть 1

Инициализация NSFileManager

Класс NSFileManager содержит метод класса с именем defaultManager, который используется для создания экземпляра класса. Например: получить ссылку на экземпляр объекта NSFileManager:

NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];

Проверка на наличие или отсутствие файла

Класс NSFileManager содержит метод экземпляра с именем fileExistsAtPath, который проверяет, существует ли указанный файл. Метод принимает в качестве аргумента объект NSString, содержащий путь к файлу и возвращает логическое ДА или НЕТ, указывающее на наличие или отсутствие этого файла:
 
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: @"/tmp/myfile.txt" ] == YES)
        NSLog (@"Файл присуствует");
else
        NSLog (@"Файл не найден");
 
Сравнение содержимого двух файлов

Содержимое двух файлов можно сравнить на равенство с использованием метода contentsEqualAtPath. Этот метод принимает в качестве аргументов пути к двум файлам, которые будут сопоставлены и возвращает логическое ДА или НЕТ, чтобы указать, соответствует ли содержимое этих файлов:
 
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
if ([filemgr contentsEqualAtPath: @"/tmp/myfile.txt" andPath: @"/tmp/sales.txt"] == YES)
        NSLog (@"Содержимое файлов одинаково");
else
        NSLog (@"Содержимое файлов разное");
 
Проверка прав файла на чтение/запись/запуск/удаление

Большинство операционных систем обеспечивают определенный уровень контроля доступа к файлу. Самый быстрый способ узнать, может ли Ваше приложение иметь доступ к использованию файла - isReadableFileAtPath, isWritableFileAtPath, isExecutableFileAtPath и метод isDeletableFileAtPath. Каждый метод принимает один аргумент в виде пути к файлу и возвращает логическое ДА или НЕТ. Например, следующий фрагмент кода проверяет ести ли доступ к файлу на запись в него:
 
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
if ([filemgr isWritableFileAtPath: @"/tmp/myfile.txt"]  == YES)
        NSLog (@"Разрешено для записи/редактирования данных");
else
        NSLog (@"Только чтение");
 
Перемещение/Удаление файла

Файл может быть переименован (при условии, соответствующих прав) с использованием метода moveItemAtURL. Этот метод возвращает логическое ДА или НЕТ и принимает в качестве аргументов путь для файла, который будет перемещен, путь назначения и дополнительный объект NSError, в который будет помещена информация, описывающая любые ошибки, возникающие в процессе эксплуатации. Если информация по ошибкам не требуется, этот аргумент может быть установлен в NULL. Обратите внимание, что если по такому пути файл уже существует, то эта операция не будет выполнена.
 
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
NSURL *oldPath = [NSURL fileURLWithPath:@"/tmp/myfile.txt"];
NSURL *newPath= [NSURL fileURLWithPath:@"/tmp/newfile.txt"];
[filemgr moveItemAtURL: oldPath toURL: newPath error: nil];
 
Продолжение в следующей части.