iOS 应用开发

iOS 应用开发(五)OC 面向对象之内存管理

2022-12-18  本文已影响0人  HiCloudKK

(一)概述

任何继承了 NSObject 的对象,都需要进行内存管理。
每个 OC 对象都有自己的引用计数器(4个字节),是一个整数,表示“对象被引用的次数”。对象刚创建的时候,引用计数器默认为1;当引用计数器为0时,对象占用的内存就会被系统回收。


注意:当对象被销毁时,系统会自动向对象发送一条 dealloc 消息。

// 打开“Build Setting”,找到“Objective-C Automatic Reference Counting”项,将它的值设置成“NO”
// 避免 ARC forbids explicit message* 编译错误
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;

- (id)initWith:(int)age;
@end
-------------------------------------------------------
#import "Person.h"
@implementation Person
+ (void)load {
    NSLog(@"Person----load");
}

#pragma mark - init
- (id)initWith:(int)age {
    if (self = [super init]) {
        self->_age = age;
    }
    return self;
}

#pragma mark - toString
- (NSString *)description {
    return [NSString stringWithFormat:@"<Person: age=%d>",  self->_age];
}

#pragma mark - recycle
- (void)dealloc {
    NSLog(@"对象被回收");
    [super dealloc];
}
@end
-------------------------------------------------------
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    // 创建对象后,refer_counter 默认为1
    Person *p = [[Person alloc] initWith:20];
    NSLog(@"%@",p);

    // retain:refer_counter + 1; 并返回对象本身
//    Person *p1 = [p retain];
//    NSLog(@"age=%d",p1.age);
    
    // 获取当前 refer_counter 的值
    NSUInteger count = [p retainCount];
    NSLog(@"count=%ld",count);// count=1
    
    // release:refer_counter -1
    // 此时引用计数器为0,对象占用的内存就会被系统回收。
    // 触发 dealloc 方法调用(对应 alloc)
    [p release];
    // 报"message sent to deallocated instance 0xxxxxx"
    p.age = 10;
    // 指针p变成空指针
    p = nil;
    
    // EXC_BAD_ACCESS: 访问了一块已经被回收的内存
    // 野指针错误:指向僵尸对象(不可用内存)的指针
    // 注意:OC 不存在空指针错误
    [p release];
    [p release];
    [p release];
    [p release];
    
    NSLog(@"%@",p);// (null)
    return 0;
}

一般来说,有 alloc/retain,就必须有 release。

#import <Foundation/Foundation.h>
@interface Student : NSObject {
    int _no;
    NSString *_name;
}

- (void)setNo:(int)no;
- (int)no;

- (void)setName:(NSString *)name;
- (NSString *)name;
@end
-------------------------------------------------------
#import "Student.h"
@implementation Student
- (void)setNo:(int)no {
    _no = no;
}

- (int)no {
    return _no;
}

- (void)setName:(NSString *)name {
    if (name !=_name) {
        // 先释放原来的,然后引用计数器+1并赋值。
        [name release];
        _name = [name retain];
    }
}

- (NSString *)name {
    return _name;
}

- (void)dealloc {
    [_name release];
    NSLog(@"Student 对象被释放");
    [super dealloc];
}
@end

(二)@property 参数

#import <Foundation/Foundation.h>
#import "Dog.h"

@interface Student : NSObject
// 【读写属性】
// (readonly):只读,只会生成getter;
// (readwrite): 可读可写,setter getter同时生成(默认)
@property (readonly,nonatomic) int no;
// 【setter 内存管理相关参数】
// assign: 生成的setter方法(默认参数,直接赋值,适用于非oc对象)
// retain: 生成的setter方法(先判断新旧对象是否为同一个对象,若不是则release旧值,再retain新值。)
// copy: release 旧值,copy 新值(一般用于 NSString ,block)。
@property (copy,nonatomic) NSString *name;
//(可自定义 setter(注意要带冒号)/getter 方法名,但不推荐)
@property (nonatomic,retain,setter=setDog2:,getter=getDog2) Dog *dog;
// 【多线程管理】
// nonatomic:性能高(推荐)
// atomic:性能低(默认)
@property (nonatomic,getter=isRich) BOOL rich;
@end
-------------------------------------------------------
#import "Student.h"
@implementation Student

- (void)dealloc {
    [_name release];
    [_dog release];
    NSLog(@"Student 对象被释放");
    [super dealloc];
}
@end
-------------------------------------------------------
// test...
Student *student = [[Student alloc] init];
student.rich = YES;
NSLog(@"student isRich=%d",student.isRich);
Dog *dog = [[Dog alloc] init];
student.dog2 = dog;
NSLog(@"%@",student.getDog2);
[dog release];
[student release];

@class 仅仅是告诉编译器,XXX 是一个类,一般用于相互依赖(为避免对象无法释放,方法1(非arc):一方用 retain,另一方用 assign; 方法2(arc) :一方用strong,一方用weak)。

#import <Foundation/Foundation.h>
@class Car;// 使用@class,不用#import
@interface Person : NSObject
@property (nonatomic,retain) Car *car;
@end
-------------------------------------------------------
#import "Person.h"
#import "Car.h"
@implementation Person
- (void)dealloc {
    [_car release];
    NSLog(@"Person对象被释放");
    [super dealloc];
}
@end
-------------------------------------------------------
#import <Foundation/Foundation.h>
@class Person;
@interface Car : NSObject
@property (nonatomic,assign) Person *person;
@end
-------------------------------------------------------
#import "Car.h"
#import "Person.h"
@implementation Car
- (void)dealloc {
//    [_person release];
    NSLog(@"Car对象被释放");
    [super dealloc];
}
@end

(三)autorelease 自动释放

autorelease 会将对象方法一个自动释放池中并返回对象本身( refer_count不变,仍然是1)。当自动释放池被销毁时,会对池子中所有对象做一次 release 操作,一般适用于小对象。

#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,assign) int age;
- (id)initWith:(int)age;
@end
-------------------------------------------------------
#import "Person.h"
@implementation Person
- (id)initWith:(int)age {
    if (self = [super init]) {
        _age = age;
    }
    return self;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"<Person: age=%d>", _age];
}

- (void)dealloc {
    NSLog(@"Person 对象被销毁");
    [super dealloc];
}
@end
-------------------------------------------------------
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    // 自动释放池(延迟执行 release,以栈结构存在)
    @autoreleasepool {// {表示创建了释放池
        // autorelease 会将对象方法一个自动释放池中并返回对象本身,
        Person *p = [[[Person alloc] initWith:20] autorelease];
        NSLog(@"%@",p);
//        [p retain];
        
        @autoreleasepool {
            Person *p1 = [[[Person alloc] initWith:30] autorelease];
            NSLog(@"%@",p1);
        }// 由于是“栈顶”,所以释放时先执行(dealloc)
    }// }表示销毁释放池

    return 0;
}

一般系统自带的方法里面没有包含使用 alloc/new/copy/ 说明返回的对象都是 autorelease 的。类方法里面,尽量使用self

(四)ARC

ARC:automatic reference counting, Xcode 编译器自动引用计数。判断标准:只要没有强指针(局部引用出栈 / 弱指针)指向对象,就会释放对象。


指针分2种:
1 强指针:默认都是强指针。
2 弱指针:__weak Person *p =[Person personWith:20];// 对象一开始被创建,就马上被释放了。

ARC 特点:
1 不允许调用release、retain、retainCount,但可以重写dealloc(注意不能调用[super dealloc]) 。
2 @property 参数
strong 成员变量是强指针,相当于原来的retain(适用于oc 对象);
weak 成员变量是弱指针 (适用于oc 对象)。


不使用 ARC.png
上一篇 下一篇

猜你喜欢

热点阅读