Свойства в Objective c. копировать и сохранять

То, что я прочитал из документа Apple, увеличит счетчик удержания на 1, а выпуск уменьшится на 1. Мне это очень ясно.

Но в случае копирования и сохранения я немного запутался.

Позвольте мне объяснить с помощью кода, который я пытаюсь сделать.

имущество ---

   @property(nonatomic, retain) NSMutableString *a;

   @property(nonatomic, copy) NSMutableString *b;


   @synthesize a = _a ,b = _b

   a=[[NSMutableString alloc]initWithString:@"Hello Ankit"];
   NSLog(@"a memory location A - %p", &a );
   b=[[NSMutableString alloc]initWithString:@"Hello Nigam"];
   NSLog(@"a memory location B- %p", &b );
   c= [[NSMutableString alloc]initWithString:@"Ankit Nigam"];
   NSLog(@"a memory location C %p",&c);


NSMutableString *temp =[[NSMutableString alloc]initWithString:@"hey"];

NSLog(@"temp = %@ %p",temp,&temp);
self.b = temp;
NSLog(@"B is now %@ %p",self.b,&b);

self.a = temp;
NSLog(@"A is now %@ %p",self.a,&a);

And i get the output as -- - -

2012-05-10 03:24:34.756 retainCountTest[2655:f803] a memory location A - 0x6d314fc
2012-05-10 03:24:34.757 retainCountTest[2655:f803] a memory location B- 0x6d31500
2012-05-10 03:24:34.764 retainCountTest[2655:f803] a memory location C 0x6d31504
2012-05-10 03:24:34.764 retainCountTest[2655:f803] temp = hey 0xbfffdd04
2012-05-10 03:24:34.764 retainCountTest[2655:f803] B is now hey 0x6d31500
 2012-05-10 03:24:34.765 retainCountTest[2655:f803] A is now hey 0x6d314fc

Но, как я понимаю из документа, объект сохранения должен иметь тот же адрес памяти, а объект копирования создаст новый объект с другим местом в памяти.

Опять же, когда я меняю журналы на ---

self.b = temp;
NSLog(@"B is now %@ %p",self.b,&_b);

self.a = temp;
NSLog(@"A is now %@ %p",self.a,&_a);
It return me a complete different memory location for both the object.

2012-05-10 03:28:49.905 retainCountTest[2688:f803] a memory location A - 0x6d4a4ac
2012-05-10 03:28:49.906 retainCountTest[2688:f803] a memory location B- 0x6d4a4b0
2012-05-10 03:28:49.907 retainCountTest[2688:f803] a memory location C 0x6d4a4b4
2012-05-10 03:28:49.907 retainCountTest[2688:f803] temp = hey 0xbfffdd04
2012-05-10 03:28:49.908 retainCountTest[2688:f803] B is now hey 0x6d4a4c0
2012-05-10 03:28:49.908 retainCountTest[2688:f803] a is now hey 0x6d4a4bc

Может ли кто-нибудь помочь мне понять полную концепцию их сохранения и копирования. Также почему я получаю такие неожиданные результаты.

Большое спасибо.


person ankit    schedule 09.05.2012    source источник
comment
Обратите внимание, что &_b - это адрес переменной _b, а не адрес объекта; таким образом, &_b и &_a будут разными независимо друг от друга и, довольно легко, могут отличаться при последующих запусках одного и того же метода в зависимости от стека вызовов.   -  person bbum    schedule 10.05.2012
comment
+1 и @ankit, это относится ко всем вашим случаям использования &. Вы не регистрируете то, что думаете о себе.   -  person Ken Thomases    schedule 10.05.2012
comment
Объекты Objective-C по своей природе являются указателями на структуру в памяти. При использовании NSLog вы можете просто использовать NSLog(@"%p", object);, который будет печатать местоположение указателя вместо метода описания объекта.   -  person Aditya Vaidyam    schedule 10.05.2012
comment
Честно говоря, если вы еще не поняли, что такое сохранение / освобождение, вы должны просто пропустить его и сразу перейти к ARC.   -  person Hot Licks    schedule 10.05.2012
comment
Это не так уж и сложно: -сохранить переданные вам объекты, которыми вы хотите владеть, как глобальный объект, и -выпустить их, когда это будет сделано. -копировать объекты, которые вы хотите оставить для себя, и назначать объекты только тогда, когда вы хотите сохранить для них слабую ссылку. ARC - это та же игра - разные названия. Фактически, я бы сказал, что для мостов, слабых типов и т. Д. ARC - ужасный выбор. Сначала его нужно улучшить.   -  person Aditya Vaidyam    schedule 10.05.2012


Ответы (2)


Свойство - это просто объявление, которое позволяет использовать методы установки, получения и средства доступа с точечным синтаксисом (скрытие переменных интерфейса).

Сам по себе он абсолютно ничего не делает, но позволяет вам использовать -[myInstance myProperty] для получения переменной или использовать -[myInstance setMyProperty:] для ее установки (да, имя метода автоматически присваивается -setProperty: и -property).

При объявлении свойства у вас есть три категории - блокировка потоков, контроль доступа и управление памятью. Вы можете выбрать только один из модификаторов для каждой категории, и если вы не выберете один, он автоматически присваивается одному из них.

@property (<thread locking>, <access control>, <memory management>) id property;

Первая категория может быть atomic или nonatomic. Модификатор atomic устанавливает блокировку @synchronized (myInstance) для переменной, чтобы обеспечить безопасность потоков. nonatomic не использует синхронизированный блок и НЕ является потокобезопасным. Если вы их не используете, автоматически устанавливается значение atomic.

Вторая категория может быть readonly или readwrite. Модификатор readwrite также позволяет изменять свойство и позволяет автоматически создавать метод -setProperty :. Когда используется модификатор readonly, нельзя использовать метод -setProperty:. Вы должны использовать внутреннюю переменную внутри объекта, чтобы установить переменную напрямую.

Третья категория может быть assign, retain и copy. Модификатор assign означает, что внутренний указатель объекта установлен на указатель, переданный в сообщение -setProperty:. Модификатор retain назначает переданный указатель и передает объекту -retain.

Модификатор copy выполняет прямую копию объекта - новый указатель на новый объект по новому адресу в памяти. Это устанавливает внутренний указатель объекта на копию переданного объекта, вызывая -copy для переданного объекта. Модификатор по умолчанию - assign, и компилятор предупредит вас, если вы не установите модификатор категории управления памятью для объекта, потому что модификатор assign для объекта не одобряется (если явно не объявлен).

Для примера на -copy, посмотрите на это:

- (void)setProperty:(GXMyObject *)property {

    // This points to the original passed object.
    GXMyObject *original = property;

    // This points to a copy of the passed object.
    CGMyObject *copied = [property copy];

    // This points to yet another copy of the passed object-
    // Independent of the other copies and original.
    _property = [property copy];

    // The anotherProperty is now different on this copy
    // than on the original and the other copies.
    _property.anotherProperty = 4;

    // This will prove that they are all individual objects.
    NSLog(@"%p, %p, %p", original, copied, _property);
}

Существует необязательный модификатор объявления имени метода, который используется следующим образом: getter = myCustomPropertyGetter и setter = myCustomPropertySetter: (двоеточие : в конце имени метода установки является обязательным, потому что оно означает, что аргумент должен быть передан).

Вторая половина - это синтезатор или динамизатор свойств. После объявления свойства (например, myView) следующим образом:

@property (nonatomic, retain) NSView *myView;

Вы бы либо: сами определили установщик и получатель; @synthesize установщик и получатель; @dynamic свойство, указывающее, что оно существует в категории или основном классе или может быть добавлено во время выполнения (заметьте, это не забавная идея и может вызвать недопустимые исключения во время выполнения).

Первый пример, написание методов самостоятельно, будет выглядеть так:

// In Apple's LLVM 3.1 Compiler, instance variables can be added 
// within {} below the @implementation as well as the @interface, 
// and in private categories (@interface GXMyClass ()) like before.
@implementation GXMyClass {
    // The internal object pointer is prefixed with an _ to avoid name confusions.
    NSView *_myView;
}

- (NSView *)myView {
    return _myView;
}

- (void)setMyView:(NSView *)myView {
    _myView = [myView retain];
}

@end

Второй пример - автосинтезировать его с помощью директивы @synthesize:

@implementation GXMyClass

// In the new Apple LLVM 3.1 Clang compiler, the = operator when used 
// next to the @synthesize directive declares an internal private 
// variable and automatically sets to that variable.
@synthesize myView = _myView;

// The internal variable name is now myOtherView, because we did not use the
// = operator to assign an internal variable name to the property.
@synthesize myOtherView;

@end

В последнем примере, возможно, наиболее запутанном, поскольку он требует использования директивы @dynamic, вам потребуется что-то вроде добавления категории или метода времени выполнения:

@interface GXMyClass (InternalMethods)
@end

@implementation GXMyClass

// The = assignment operator does not work here.
@dynamic myView;

@end

@implementation GXMyClass (InternalMethods)

- (NSView *)myView {
    return [self methodThatReturnsAnNSView];
}

- (void)setMyView:(NSView *)myView {
    [self methodThatAcceptsAnNSViewArgument:myView];
}

@end

Объявление @property требует наличия одного из трех вышеупомянутых объявлений - оно ничего не делает само по себе. Однако он позволяет использовать средства доступа с точечным синтаксисом (средства доступа, подобные Java, для установки и получения свойств).

Например, к @property (copy) NSString *myName; можно получить доступ с помощью -[myObject myName] и установить с помощью -[myObject setMyName:].

Теперь его можно установить с помощью myObjectInstance.myName = @"Bob"; и получить с помощью myObjectInstance.myName. Используя все вышеперечисленные концепции, можно создать такой объект, как этот:

// The GXBufferQueue is a queue which buffers all requests, till they are read
// asynchronously later. The backing store is an NSMutableArray to which all
// buffer writes are appended to, and from which the first object is pulled and
// returned when the buffer is read to.   
@interface GXBufferQueue

@property (nonatomic, readwrite, copy, setter = write:, getter = read) id buffer;

+ (GXBufferQueue *)queue;

@end

@implementation GXBufferQueue {
    // This queue is an internal array and is 'tacked on' to the @implementation
    // so no others can see it, and it can be marked @private so subclasses cannot
    // use it. It is also good code practice to have @interfaces composed of only
    // @properties, setters, and getters, rather than expose internal variables.
    NSMutableArray *_internalQueue;
}

+ (GXBufferQueue *)queue {
    return [[[GXBufferQueue alloc] init] autorelease];
}

- (id)init {
    if((self = [super init])) {
        _internalQueue = [[NSMutableArray alloc] init];
    }
}

- (void)write:(id)buffer {
    [_internalQueue addObject:buffer];
}

- (id)read {
    if(!(_internalQueue.count > 0)) return nil;

    id buffer = [_internalQueue objectAtIndex:0];
    [_internalQueue removeObjectAtIndex:0];
    return buffer;
}

@end

Примечание: этот код никоим образом не тестировался. Теперь, когда у вас есть GXBufferQueue, все следующее работает:

GXBufferQueue *queue = [GXBufferQueue queue];

// Option One: using the traditional message syntax:
[queue write:@"This will be now added to the buffer."];
NSLog(@"Now the string written to the queue will be read \
and removed from the queue, like a stack pop. ", [queue read]);

// Option Two: using the new dot-syntax accessors:
queue.buffer = @"As clunky as this looks, it works the same as above.";
NSLog(@"These lines work just the same as the ones above: ", queue.buffer);

Как видите, со свойствами можно многое сделать, и с ними можно сделать гораздо больше, чем просто объявление переменных. Если есть какие-либо вопросы или что-то, что сообщество хочет, чтобы я добавил / исправил / в публикации, пожалуйста, оставьте комментарий! : D

person Aditya Vaidyam    schedule 09.05.2012
comment
Еще одна сноска в вашем коде: не полагайтесь на -retainCount: во многих случаях он неисправен (включая GCD и PDO). - person Aditya Vaidyam; 10.05.2012
comment
Спасибо, Galaxas, за хорошее объяснение. Итак, из этого я могу понять, что объект типа сохранения при назначении объекту типа копии (если он изменен) будет создан новый объект копии с новым местоположением. Пожалуйста, поправьте меня, если я все еще ошибаюсь. - person ankit; 10.05.2012
comment
Да вы правы. Чтобы отобразить это, позвольте мне объяснить это так: объект, которому вы передаете -copy, больше не является тем же объектом, что и раньше: он находится в новой области памяти с тем же содержимым, что и исходный. Я обновлю свой ответ, чтобы объяснить это. - person Aditya Vaidyam; 11.05.2012
comment
Спасибо Galaxas0, Ваше объяснение мне очень помогло. - person ankit; 11.05.2012
comment
@ Galaxas0: copy может или не может вернуть новый объект - person user102008; 23.06.2012
comment
Предполагается вернуть новый объект, если вы правильно реализовали NSCopying. - person Aditya Vaidyam; 23.06.2012
comment
@ Galaxas0: Большое спасибо за краткое объяснение. - person Mubin Mall; 18.08.2014
comment
Нет, -copy не предполагается для возврата нового объекта. Это происходит только в том случае, если требуется новый экземпляр. Например, неизменяемые объекты не копируются. - person bbum; 22.12.2017

Да, retainCount бесполезен - не называйте его - и предположение, что copy всегда будет возвращать новый экземпляр, неверно.

Если неизменяемый объект отправляется copy, он обычно реализуется как:

- copy { return [self retain]; }

Т.е. нет необходимости делать фактическую копию, поскольку содержимое не может измениться.

Конечно, поскольку вы возитесь со статическими, скомпилированными в двоичный файл, строками, реализация, скорее всего, будет ближе к:

- retain { return self; }
- copy { return self; }
- (void)release { ; }
- (void)dealloc { ; }

Возможно - все вышеперечисленное - детали реализации, которые могут измениться в любой момент. Суть в том, что все вышеперечисленное выполняет договор о сохранении / выпуске / копировании / и т. Д.

person bbum    schedule 09.05.2012