Objective-C中的内存管理——手动内存管理

一、引用计数(retainCount)

1、内存的申请

alloc和new是Objective-C语言中申请内存的两种常用方式。

当我们向一个类对象发送一个alloc消息的时候,其会向操作系统在堆中申请一块内存用于存放该类的实例对象。然后将该实例对象的引用计数(retainCount)设置为1,最后返回该对象的指针。

alloc和new的区别:一句话,[Person new]即[[Person alloc]new]

2、引用计数(retainCount)

Cocoa框架中,所有继承自NSObject的子类(包括NSObject)对象中都有一个4个字节的存储空间用于存储其retainCount的值。

我们可以通向对象发送retainCount消息来获取该对象的retainCount的值。

3retainrelease

当我们向一个实例对象发送一个retain消息的时候,其引用计数(retainCount)会执行+1操作。

当我们向一个实例对象发送给一个release消息的时候,其引用计数(retainCount)会执行-1操作。

4、内存的释放

在OC中,当我们向一个对象发送release消息是,其先使得该对象的retainCount属性-1,然后在判断该对象的retainCount是否为0。如果为0则在release方法中释放该对象的内存。

对NSObject对象的retain和release实现的猜想

@implementation NSObject

{

Class isa  OBJC_ISA_AVAILABILITY;

int retainCount;

}

-(instancetype)retain

{

self->retainCount++;

return self;

}

-(void)release

{

self->retainCount--;

if (self->retainCount==0) {

[self dealloc];

free(self);

}

}

@end

Tip:如果在一个局部作用域中申请了一块内存,但是当该作用域结束时,系统不会自动调用该对象的release方法。

示例:

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

Person* kaka;

if (YES) {

kaka = [Person alloc];

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

NSLog(@"end");

}

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

return 0;

}

代码执行结果:

2015-11-23 13:31:13.447 Test[4576:53048] 调用了allc

2015-11-23 13:31:13.448 Test[4576:53048] kaka   retainCount = 1

2015-11-23 13:31:13.448 Test[4576:53048] end

2015-11-23 13:31:13.449 Test[4576:53048] kaka retainCount = 1

Program ended with exit code: 0

可以看出,该系统并不会自动向该对象发送release消息。

注意:当一个对象已经被释放之后,再次对该对象发送release消息,会造成运行时异常。因为该指针指向的内存地址已经不存在了。

错误代码 :

Person* kaka;

kaka = [Person alloc];

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

[kaka release];

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

因此,当一个对象的已经被释放之后,我们应该将该指针赋值为nil

问题:当一个对象被释放之后,我们不能向其发送retainCount消息,如何判断其已经被释放呢?

解决方案:在发送release消息之前,先发送retainCount消息,判断其retainCount是否为1.如果是不是1则直接发送release消息,否则发送release消息,再将该指针置为nil。

示例:

Person* kaka;

kaka = [Person alloc];

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

[kaka retain];

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

if ([kaka retainCount]==1) {

[kaka release];

kaka = nil;

}else{

[kaka release];

}

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

if ([kaka retainCount]==1) {

[kaka release];

kaka = nil;

}else{

[kaka release];

}

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

代码执行结果:

2015-11-23 13:54:23.405 Test[5913:65897] 调用了allc

2015-11-23 13:54:23.406 Test[5913:65897] kaka retainCount = 1

2015-11-23 13:54:23.406 Test[5913:65897] kaka retainCount = 2

2015-11-23 13:54:23.406 Test[5913:65897] kaka retainCount = 1

2015-11-23 13:54:23.406 Test[5913:65897] -[Person dealloc]

2015-11-23 13:54:23.406 Test[5913:65897] kaka retainCount = 0

可以看出,通过这种方式可以避免对象被释放后,指针没有及时地置为nil

二、NSAutoreleasePool

1NSAutoreleasePool的接口

@interface NSAutoreleasePool : NSObject {

@private

void    *_token;

void    *_reserved3;

void    *_reserved2;

void    *_reserved;

}

+ (void)addObject:(id)anObject;

- (void)addObject:(id)anObject;

- (void)drain;

@end

2NSAutoreleasePool对象介绍

由NSAutoreleasePool的接口我们可以知道,我们能够向NSAutoreleasePool对象发送的消息只有两个,即addObject和drain。

因此,我们可以从其方法addObject进行猜测,NSAutoreleasePool对象内部有一个属性NSArray* _objects,用于存放其它任意对象,而其方法addObject就是将其参数添加到其_objects属性中。

由APPLE官方文档对NSAutoreleasePool的drain的介绍

"In a reference-counted environment, this method behaves the same as release. Since an autorelease pool cannot be retained (see retain), this therefore causes the receiver to be deallocated. When an autorelease pool is deallocated, it sends a release message to all its autoreleased objects. If an object is added several times to the same pool, when the pool is deallocated it receives a release message for each time it was added."

我们可以发现,这个方法的作用就是对其遍历其_objects属性,依次向对象发送release消息。

最后我们可以大胆的猜测NSAutoreleasePool类的基本实现如下:

@implementation NSAutoreleasePool

{

NSMutableArray* _objects;

}

-(instancetype)init

{

self = [super init];

self->_objects = [NSMutableArray new];

return self;

}

-(void)addObject:(id)anObject

{

[self->_objects addObject:anObject];

}

-(void)drain

{

for (int index = [self->_objects count]; index>0; index--) {

[[self->_objects objectAtIndex:(index-1)] release];

}

}

-(void)dealloc

{

[self drain];

}

@end

3、验证我们的猜测

验证代码:

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

NSAutoreleasePool *autoReleasePool = [NSAutoreleasePool new];

Person* kaka = [[Person alloc] initWithName:@"kaka"];

Person* maomao = [[Person alloc] initWithName:@"maomao"];

[autoReleasePool addObject:kaka];

[autoReleasePool addObject:maomao];

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

NSLog(@"maomao retainCount = %ld",[maomao retainCount]);

[autoReleasePool drain];

return 0;

}

代码执行结果:

2015-11-23 15:01:07.518 Test[10150:105757] +[Person alloc]

2015-11-23 15:01:07.519 Test[10150:105757] -[Person initWithName:] kaka

2015-11-23 15:01:07.520 Test[10150:105757] +[Person alloc]

2015-11-23 15:01:07.520 Test[10150:105757] -[Person initWithName:] maomao

2015-11-23 15:01:07.520 Test[10150:105757] *** -[NSAutoreleasePool addObject:]: Do not use this instance method on specific pools -- just use -autorelease instead.

2015-11-23 15:01:07.520 Test[10150:105757] kaka retainCount = 1

2015-11-23 15:01:07.520 Test[10150:105757] maomao retainCount = 1

2015-11-23 15:01:07.520 Test[10150:105757] -[Person release] maomao

2015-11-23 15:01:07.520 Test[10150:105757] -[Person dealloc] maomao

2015-11-23 15:01:07.520 Test[10150:105757] -[Person release] kaka

2015-11-23 15:01:07.520 Test[10150:105757] -[Person dealloc] kaka

由验证代码的执行结果,我们可以发现我们的猜测是对的。而且在其addObject方法中,并没有对传入的对象进行retain(引用计数+1)。在其drain方法中,是按照栈的顺序来向对象发送release消息

4、向NSObject对象发送autorelease消息

我们先对autorelease方法的实现做一个猜想

@implementation NSObject

-(instancetype)autorelease

{

[NSAutoreleasePool addObject:self];

return self;

}

@end

在我们的猜想中,autorelease方法是先NSAutoreleasePool类对象发送一个addObject消息(在前面NSAutoreleasePool的接口中我们可以发现,其提供了向NSAutoreleasePool类对象发送addObject消息的接口,而且我们可以猜测其作用也是将传入的对象添加到NSAutoreleasePool实例对象的_objects属性中),然后再返回self

验证:对比验证

代码一:

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

NSAutoreleasePool *autoReleasePool = [NSAutoreleasePool new];

Person* kaka = [[Person alloc] initWithName:@"kaka"];

Person* maomao = [[Person alloc] initWithName:@"maomao"];

[kaka autorelease];

[maomao autorelease];

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

NSLog(@"maomao retainCount = %ld",[maomao retainCount]);

//    [autoReleasePool drain];

return 0;

}

代码一的执行结果:

2015-11-23 15:16:57.852 Test[10999:113705] +[Person alloc]

2015-11-23 15:16:57.853 Test[10999:113705] -[Person initWithName:] kaka

2015-11-23 15:16:57.853 Test[10999:113705] +[Person alloc]

2015-11-23 15:16:57.853 Test[10999:113705] -[Person initWithName:] maomao

2015-11-23 15:16:57.853 Test[10999:113705] kaka retainCount = 1

2015-11-23 15:16:57.853 Test[10999:113705] maomao retainCount = 1

Program ended with exit code: 0

代码二:

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

NSAutoreleasePool *autoReleasePool = [NSAutoreleasePool new];

Person* kaka = [[Person alloc] initWithName:@"kaka"];

Person* maomao = [[Person alloc] initWithName:@"maomao"];

[kaka autorelease];

[maomao autorelease];

NSLog(@"kaka retainCount = %ld",[kaka retainCount]);

NSLog(@"maomao retainCount = %ld",[maomao retainCount]);

[autoReleasePool drain];

return 0;

}

代码二的执行结果:

2015-11-23 15:19:39.184 Test[11155:115422] +[Person alloc]

2015-11-23 15:19:39.185 Test[11155:115422] -[Person initWithName:] kaka

2015-11-23 15:19:39.185 Test[11155:115422] +[Person alloc]

2015-11-23 15:19:39.185 Test[11155:115422] -[Person initWithName:] maomao

2015-11-23 15:19:39.185 Test[11155:115422] kaka retainCount = 1

2015-11-23 15:19:39.185 Test[11155:115422] maomao retainCount = 1

2015-11-23 15:19:39.186 Test[11155:115422] -[Person release] maomao

2015-11-23 15:19:39.186 Test[11155:115422] -[Person dealloc] maomao

2015-11-23 15:19:39.186 Test[11155:115422] -[Person release] kaka

2015-11-23 15:19:39.186 Test[11155:115422] -[Person dealloc] kaka

结论:[NSAutoreleasePool addObject:self];应该是将传入的参数添加到一个NSAutoreleasePool实例对象的_objects属性中(通过调用NSAutoreleasePool实例对象的addObject方法)。

经过我反复的验证,我发现前面的关于NSAutoreleasePool实例对象drain的实现并不完全正确,最后我关于NSAutoreleasePool类的实现如下

@implementation NSAutoreleasePool

static NSMutableArray* objects;

static NSMutableArray* autoReleaseRools;

+ (void)initialize

{

autoReleaseRools = [NSMutableArray new];

objects = [NSMutableArray new];

}

-(instancetype)init

{

self = [super init];

[autoReleaseRools addObject:self];

return self;

}

-(void)addObject:(id)anObject

{

[objects addObject:anObject];

}

+(void)addObject:(id)anObject

{

[objects addObject:anObject];

}

-(void)drain

{

for (int index = [objects count]; index>0; index--) {

[[objects objectAtIndex:(index-1)] release];

}

}

-(void)dealloc

{

[self drain];

}

@end

5NSAutoreleasePool的作用

先前我们当我们需要释放一个一个对象时需要向该对象发送release消息,直到其引用计数为0

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

Person* kaka = [[Person alloc] initWithName:@"kaka"];

[kaka retain];

[kaka retain];

if ([kaka retainCount]==1) {

[kaka release];

kaka = nil;

}else{

[kaka release];

}

if ([kaka retainCount]==1) {

[kaka release];

kaka = nil;

}else{

[kaka release];

}

if ([kaka retainCount]==1) {

[kaka release];

kaka = nil;

}else{

[kaka release];

}

return 0;

}

现在我们可以向该对象发送一个autorelease消息(建议在创建一个对象的时候就发送autorelease消息,且应该只发送给一次,多次发送可能会造成程序异常。),将该对象添加到NSAutoreleasePool中。最后我们我们当我们只需要在NSAutoreleasePool对象被释放前,所有对象的retainCount为<=1即可

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

NSAutoreleasePool* autoReleasePool = [NSAutoreleasePool new];

Person* kaka = [[[Person alloc] initWithName:@"kaka"] autorelease];

[kaka retain];

[kaka retain];

[kaka release];

[kaka release];

NSLog(@"autoReleasePool将被释放");

[autoReleasePool release];

return 0;

}

三、NSCopying协议和NSMutableCopying协议

1NSObject对象的copymutableCopy

由NSObject类的接口我们可以发现,我们可以向NSObject对象发送copy和mutableCopy消息

- (id)copy;

- (id)mutableCopy;

Apple官方文档对这两个方法的注释如下

-(id)copy

Return Value

The object returned by the NSCopying protocol method copyWithZone:.

Discussion

This is a convenience method for classes that adopt the NSCopying protocol. An exception is raised if there is no implementation for copyWithZone:.

NSObject does not itself support the NSCopying protocol. Subclasses must support the protocol and implement the copyWithZone: method. A subclass version of the copyWithZone: method should send the message to super first, to incorporate its implementation, unless the subclass descends directly from NSObject.

-(id)mutableCopy

Return Value

The object returned by the NSMutableCopying protocol method mutableCopyWithZone:.

Discussion

This is a convenience method for classes that adopt the NSMutableCopying protocol. An exception is raised if there is no implementation for mutableCopyWithZone:.

因此我们可以发现,NSObject对象的copy和mutableCopy方法就是分别向其自身发送copyWithZone和mutableCopy消息。但是NSObject对象本身并没有实现copyWithZone和mutableCopy方法。因此,当我们向一个对象发送copy和mutableCopy的消息时,需要确保该对象实现了copyWithZone和mutableCopyWithZone方法。

根据Apple官方文档中对NSMutableCopying协议的介绍

The NSMutableCopying protocol declares a method for providing mutable copies of an object. Only classes that define an “immutable vs. mutable” distinction should adopt this protocol. Classes that don’t define such a distinction should adopt NSCopying instead.

我们可以发现,对于那些定义了“可变”和“不可变”的类才应该都实现NSMutableCopying协和和NSCopying协议。如果,不存在“可变”和“不可变”的类只需要实现NSCopying协议即可。

总之copyWithZone和mutableCopyWithZone都是实例化一个新对象,该新对象的所有属性和当前对象的所有属性完全相同。但是copyWithZone返回的事"Immutable",而mutableCopyWithZone返回的是“mutable”

2、实现NSCopying协议

现在我们来实现一个Book类,并为其实现一下NSCopying协议

@implementation Book:NSObject

{

NSString* _bookName;

NSString* _bookAuthor;

}

-(id)copyWithZone:(NSZone*)zone

{

Book* newBook = [Book allocWithZone:zone];

newBook->_bookAuthor = self->_bookAuthor;

newBook->_bookName = self->_bookName;

return newBook;

}

@end

3Foundation框架中的IMMutable

Foundation框架中定义了一些集合类,为了尽可能地减少内存开销,这些集合类分为MMutable和Mutable。

IMMutable:NSString、NSArray、NSSet、NSDictionary

Mutable:NSMutableString、NSMutableArray、NSMutableSet、NSMutableDictionary

[1]其中IMMutable集合对象一旦被创建,则不可以对其成员进行增删操作,而Mutable集合对象则可以对其成员进行增删操作

[2]IMMutable集合对象和Mutable集合对象最大的区别在于IMMutable集合对象copyWithZone方法的实现并未依照NSCopying协议的约定,而是先对其自身发送retain消息,然后返回自身对象的指针。

以NSArray对象的copyWithZone实现为例:

@implementation NSArray

-(id)mutableCopy

{

[self retain];

return self;

}

@end

验证:

NSDictionary* dict = @{@"one":@1,@"two":@"2"};

NSLog(@"dict retainCount:%ld",[dict retainCount]);

NSDictionary* dict2 = [dict copy];

NSMutableArray* dict3 = [dict mutableCopy];

NSLog(@"dict retainCount:%ld",[dict retainCount]);

if (dict==dict2) {

NSLog(@"dict==dict2");

}

if (dict3!=dict) {

NSLog(@"dict3!=dict");

}

NSMutableDictionary* mutableDict = [NSMutableDictionary dictionaryWithDictionary:dict];

NSLog(@"mutableDict:retainCount %ld",[mutableDict retainCount]);

NSDictionary* mutableDict2 = [mutableDict copy];

NSLog(@"mutableDict:retainCount %ld",[mutableDict retainCount]);

if (mutableDict==mutableDict2) {

NSLog(@"mutableDict==mutableDict2");

}else{

NSLog(@"mutableDict!=mutableDict2");

}

代码执行结果:

2015-11-23 17:52:04.148 Test[20157:198839] dict retainCount:1

2015-11-23 17:52:04.150 Test[20157:198839] dict retainCount:2

2015-11-23 17:52:04.150 Test[20157:198839] dict==dict2

2015-11-23 17:52:04.150 Test[20157:198839] dict3!=dict

2015-11-23 17:52:04.151 Test[20157:198839] mutableDict:retainCount 1

2015-11-23 17:52:04.151 Test[20157:198839] mutableDict:retainCount 1

2015-11-23 17:52:04.152 Test[20157:198839] mutableDict!=mutableDict2

Program ended with exit code: 0

因此当我们向Foudation框架中的不可变集合类型发送copy消息时,其返回的是原对象的指针,且该对向的retainCount进行了+1操作

[3]NSFoundatioin框架还对NSString对象进行了进一步优化。

当我们创建一个NSString对象时,其retainCount为-1。

示例代码:

NSString* str = [NSString stringWithString:@"Hello World"];

[str retain];

NSLog(@"str retainCount:%ld",[str retainCount]);

NSMutableString* mutableStr = [NSMutableString stringWithString:@"Hello World"];

NSLog(@"mutableStr retainCount:%ld",[mutableStr retainCount]);

NSLog(@"执行copy");

NSString* str2 = [str copy];

NSLog(@"str retainCount:%ld",[str retainCount]);

if (str2==str) {

NSLog(@"str2==str1");

}

NSString* mutableStr2 = [mutableStr copy];

NSLog(@"mutableStr retainCount:%ld",[mutableStr retainCount]);

if (mutableStr2==mutableStr) {

NSLog(@"mutableStr2==mutableStr");

}else{

NSLog(@"mutableStr2!=mutableStr1");

}

代码执行结果:

2015-11-23 17:55:16.578 Test[20327:200783] str retainCount:-1

2015-11-23 17:55:16.579 Test[20327:200783] mutableStr retainCount:1

2015-11-23 17:55:16.579 Test[20327:200783] 执行copy

2015-11-23 17:55:16.579 Test[20327:200783] str retainCount:-1

2015-11-23 17:55:16.579 Test[20327:200783] str2==str1

2015-11-23 17:55:16.579 Test[20327:200783] mutableStr retainCount:1

2015-11-23 17:55:16.579 Test[20327:200783] mutableStr2!=mutableStr1

Program ended with exit code: 0

由上面的代码我们可以发现,当我们向一个NSString对象发送copy消息时,其retainCount不变,且返回的指针就是当前对象的指针。因此,我们可以猜测NSString的copyWithZone方法实现如下:

@implementation NSString

-(id)copyWithZone:(NSZone *)zone

{

return self;

}

@end

四、属性特性

1、数据隐藏

Matt Weisfeld大师在其写的《The Object-Oriented Thought Proccess》(中文版为《写给大家看的面向对象编程书》)谈到,创建对象的其中一个作用就是实现数据隐藏,数据隐藏包括限制对属性和方法的访问,并且所有的属性都应该隐藏。因此在类的接口中,将属性声明为public会破坏数据隐藏的概念。

如果对象的某个属性需要和外界进行交互,可以通过在类的接口中提供该属性的获取方法和设置方法。在面向对象编程的术语中,获取方法和设置方法就是该属性的property(特性)

注意:获取方法和设置方法的概念支持数据隐藏的概念。因为其他对象不能直接处理另一个对象内部的数据,所以获取方法和设置方法提供了对对象的数据的一种受控访问。获取方法和设置方法有时分别称为访问方法(accessor method)和修改方法(mutator method)

2、为属性添加property

下面我们将定一个Server类,并为其某些属性添加property

@interface Server : NSObject

-(NSString*)ipAdderss;

-(void)postWithData:(NSData*)data;

@end

@implementation Server

{

NSString* _hostName;

NSString* _ipAddress;

}

-(NSString*)ipAddress

{

return self->_ipAddress;

}

-(void)postWithData:(NSData*)data

{

[self handlePostRequest];

}

-(void)handlePostRequest

{

}

在这个Server类的接口中,我们提供了关于Server对象内部的_ipAddress属性的访问方法ipAddress.这个ipAddress方法成为_ipAddress属性的property

3、访问方法和设置方法在OC中的正确使用

下面我们将分别定义两个类:Person和House

@interface Person : NSObject

-(instancetype)initWithName:(NSString*)name;

-(NSString*)name;

@end

@implementation Person

{

NSString* _name;

}

-(NSString*)description

{

return [NSString stringWithFormat:@"Hello I‘m %@",self->_name];

}

-(instancetype)initWithName:(NSString *)name

{

self = [super init];

[name retain];

self->_name = name;

return self;

}

-(NSString*)name

{

return self->_name;

}

@end

@class Person;

@interface House : NSObject

-(Person*)owner;

-(void)setOwner:(Person*)owner;

@end

@implementation House

{

Person* _owner;

}

-(instancetype)initWithOwner:(Person*)owner

{

self = [super init];

[Owner retain];

self->_owner = owner;

return self;

}

-(Person*)owner

{

return self->_owner;

}

-(void)setOwner:(Person*)owner

{

//使原对象的retainCount-1

[self->_owner release];

//使新对象的retainCount+1

[owner retain];

//赋值

self->_owner = owner;

}

@end

我们在定义House和Person的时候,为对象的属性进行赋值时,一般会对属性指向的就对象发送release,然后对新对象进行retain,再将该属性指向新对象。

4Objective-C2.0新的关键字@property

在Objective-C2.0,可以使用@property关键字快速生成属性的获取方法和设置方法。

[1]@property的参数

我们在使用@property关键字的时候,还可以指定参数,使得编译器根据指定的参数生成获取方法和设置方法

是否生成设置方法readwrite和readonly

使用readwrite作为参数时,则会同时生成访问方法和设置方法。

使用readonly作为参数时,则不会生成设置方法,只会生成访问方法

设置方法和访问访问的方法名setter=、getter=

可以在setter和getter后面指定生成的设置方法名和访问方法名。如果不指定则getter方法名默认为属性名,setter方法名默认为set属性名。

获取方法的参数atomic和nonatomic

使用atomic作为参数时,则多个线程不可以同时访问@property关联的属性,但是会影响CPU的性能。

使用nonatomic作为参数时,则多个线程可以同时访问@property关联的属性。因为在iOS平台和Mac OSX平台中,属性默认是atomic。但是在iOS开发中,我们推荐使用nonatomic。

设置方法的参数retain、copy、assign

使用retain作为参数时,则会对原属性指向的对象发送release消息,然后在向新对象发送retain消息,最后再将属性指向新对象。

其生成的代码相当于:

-(void)setOwner:(Person*)owner

{

//使原对象的retainCount-1

[self->_owner release];

//使新对象的retainCount+1

[owner retain];

//赋值

self->_owner = owner;

}

使用copy作为参数时,则会向原属性指向的对象发送release消息,然后再向新对象发送copy消息,最后再将属性指向新对象。

其生成的代码相当于:

-(void)setOwner:(Person*)owner

{

//使原对象的retainCount-1

[self->_owner release];

//使新对象的retainCount+1

[owner copy];

//赋值

self->_owner = owner;

}因此通常我们在使用copy作为参数时需要确定该参数的类型是否实现了NSCopying协议。

使用assign作为参数,则会直接将原属性指向新对象或者基本数据类型。

其生成的代码相当于

-(void)setOwner:(Person*)owner

{

self->_owner = owner;

}

[2]@property的作用

根据参数在类的接口中生成指定的获取方法和设置方法的声明,在接口中生成属性的声明(访问控制为private)。在类的实现中生成获取方法和设置方法的实现

提供点语法机制:在OC中slef指针的本质上就是一个结构体指针,因此访问其内部的成员变量只能通过“self->成员变量名”的形式进行访问。而使用了@property之后,可以通过self.property的形式进行访问,编译器会自动将其替换为对相关方法的调用。

示例:

self.owner = owner;

相当于

[self setOwner:owner];

owner = self.owner

相当于

owner = [self owner];

5@synthesize@dynamic

@synthesize关键字会根据其后面指定的参数,在类的实现中声明指定的属性。一般不使用,因为在Xcode4的Apple LLVM compiler 4.0新编译器中会自动为属性添加@synthesize。

示例:

@synthesize owner = _owner

编译器会根据将接口中@property (nonatomic,retain)Person* owner,在类的实现中声明Person* _owner;然后,将生成的获取方法和设置方法关联到_owner属性。

@dynamic关键字,表示该@property的setter方法和getter方法有其他方式生成,比如super类,比如程序员自己定义。

写在后面

@property关键字为我们开发人员带来了不少便利,但我觉得@property带来的点语法机制不但破坏了Objective-C的设计哲学,还容易误导初学者。

时间: 2024-10-08 20:04:32

Objective-C中的内存管理——手动内存管理的相关文章

Objective-C 【内存管理&amp;手动内存管理 综述】

------------------------------------------- 内存管理 (1)Objective-C的内存管理 栈区    存放局部变量(由于基本数据类型占用的存储空间是固定的,由系统去分配,我们不用去管,故栈区存放基本数据类型,) 堆区    存放程序运行过程中动态分配的内存空间(对象类型是程序运行过程中动态分配的,他们的大小不固定.比如说是我们Person new申请来的,存放在堆区,也是我们需要管理的) ★所以内存管理的范围是   继承了NSObject的所有对象

iOS-旧项目中手动内存管理(MRC)转ARC

在ARC之前,iOS内存管理无论对资深级还是菜鸟级开发者来说都是一件很头疼的事.我参 加过几个使用手动内存管理的项目,印象最深刻的是一个地图类应用,由于应用本身就非常耗内存,当时为了解决内存泄露问题,每周都安排有人值班用 Instruments挨个跑功能,关键是每次都总能检查出来不少.其实不管是菜鸟级还是资深级开发者都避免不了写出内存泄露的代码,规则大家都懂,可是 天知道什么时候手一抖就少写了个release? 好在项目决定转成ARC了,下面将自己转换的过程和中间遇到的问题写出来和大家共享,希望

object-C 手动内存管理(MRC)

object-C的内存管理和javascript的垃圾回收不一样,今天总结下手动内存管理,ARC的后边补上. 1:基本铺垫 oc采用引用计数来表示对象的状态,比如通过init创建出来的一个对象引用计数为1,如果想让它释放则对这个对象发送一条release消息,则引用计数-1,那怎么+1呢,给这个对象发送retain消息. 对象操作 Object-C方法 生成并持有对象 alloc/new/copy/mutableCopy方法 持有对象 retain方法 释放对象 release方法 废弃对象 d

手动内存管理和自动释放池

手动内存管理 在进行内存管理的时候要注意内存管理的准则:谁开辟内存,谁释放内存(谁污染的谁治理) .开辟内存之后,对象的引用计数为1,只有继承自NSObject的对象才有内促管理的概念, 当对象引用计数为0的时候对象的内存会被清理. 下列关键字会开辟内存,对象引用计数器+1 alloc new copy mutableCopy 下列是内存管理的相关方法. retain :保留对象,对象的引用计数器+1. release : 释放对象,对象引用计数器-1. retainCount : 获取对象当前

黑马程序员---OC基础6【内存管理】【手动内存管理】【单、多个对象的内存管理】【*@property参数】【@class的使用】【NSString类的内存管理】【autorelease使用】

------- iOS培训.Android培训.Java培训.期待与您交流! ---------- [内存管理] 1.内存管理概念 由于移动设备内存及其有限,所以每个app所占的内存也是有限的 需要回收一些不使用的空间 2.OC内存管理的范围 管理任何继承NSOject的对象,对其他的基本数据类型无效 主要管理堆区中的对象的内存管理   3.内存管理的原理 1)对象所有权概念 任何对象都可以能拥有一个或多个所有者,只要一个对象至少还拥有一个所有者,他就会继续存在 cocoasu所有权策略 任何自

【iOS开发-33】学习手动内存管理临时抛弃ARC以及retain/assign知识——iOSproject师面试必考内容

我们为什么须要内存管理?当使用内存达到40M和45M时候会发出警告,假设不处理,占用内存达到120M时直接强制关闭程序. 所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大. (1)创建一个对象的过程:先分配内存空间存储对象:初始化成员变量:返回对象的指针. (2)对象在创建时,内部会自己主动创建一个引用计数器retainCount,当retainCount=0时,系统会回收当前对象,retainCount是唯一推断标记.release会-1.retain会+1,retain后返回的是自

小白学开发(iOS)OC_手动内存管理(2015-08-02)

// //  main.m //  手动内存管理 // //  Created by admin on 15/8/3. //  Copyright (c) 2015年 admin. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { /* 当我们创建一个对象时: 1. 分配内存空间,存储对象

iOS手动内存管理知识总结及发散思维

[版权声明] 此文版权归作者Vince Yuan (vince.yuan#gmail.com)所有.欢迎非营利性转载,转载时必须包含原始链接http://vinceyuan.cnblogs.com/,且必须包含此版权声明的完整内容. 版本 1.1  发表于2010-03-08 [注] 本文会以作者Vince Yuan的文章内容为基础,本人添加括号备注. 前言 初学objectice-C的朋友都有一个困惑,总觉得对objective-C的内存管理机制琢磨不透,程序经常内存泄漏或莫名其妙的崩溃.我在

【iOS开发-33】学习手动内存管理暂时抛弃ARC以及retain/assign知识——iOS工程师面试必考内容

我们为什么需要内存管理?当使用内存达到40M和45M时候会发出警告,如果不处理,占用内存达到120M时直接强制关闭程序.所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大. (1)创建一个对象的过程:先分配内存空间存储对象:初始化成员变量:返回对象的指针. (2)对象在创建时,内部会自动创建一个引用计数器retainCount,当retainCount=0时,系统会回收当前对象,retainCount是唯一判断标记.release会-1,retain会+1,retain后返回的是自己se