美文网首页iOS
OC基础知识

OC基础知识

作者: 小怪兽大作战 | 来源:发表于2021-03-25 22:54 被阅读0次

    OC中内存分为五大区域

    栈:存储局部变量和指针
    堆:创建对象
    BSS:存储未初始化的全局变量和静态变量
    数据段(常量区):存储已经初始化的全局变量、静态变量、常量数据
    代码段:存储程序的代码

    类加载

    当类被第一次加载的时候,会将类的代码存储到代码段中,这个过程叫类加载。
    只有第一次访问的时候,才有类加载。
    一旦类被加载到程序中,直到程序结束的时候才会被回收

    创建对象

    [[NSObject alloc] init];
    alloc分配内存 init初始化

    NSObject.+alloc 
    
    struct obj_layout {
      NSUInteger retained;
    }
    
    + (id) alloc {
        int size = sizeof(struct obj_layout);
        struct obj_layout *p = (struct obj_layout *)calloc(1, size);
        return (id)(p + 1);
    }
    

    销毁对象

    NSObject.release中,首先调用NSDecrementExtraRefCountWasZero使retrain-1。如果retrainCount == 0,调用release方法释放内存

    NSObject.dealloc
    释放内存

    GNUstep

    alloc类方法通过 struct obj_layout中对retrained整数来保存引用记数,并将其写入对象内存头部,将该对象内存块全部置0后返回

    也就是,一个对象的引用记数,放在这个类地址的最前面。

    apple

    使用一个散列表保存所有的引用记数。将对象的地址映射到散列表中。

    autorelease

    autorelease会在超过对象的作用域之后,自动调用对象的release方法(记数-1。如果记数==0,调用dealloc方法)

    GNUstep中autorelease的实现

    [obj autorelease]方法的原理就是把对象添加到NSAutoreleasePool中

    [NSAutoreleasePool drain]就是调用填入的所有对象的release方法

    apple的实现

    RunLoop和线程

    在iOS开发中,会遇到两个线程对象:pthread_t 和 NSThread,NSThread只是对pthread的封装,两者肯定是一一对应的,我们可以通过pthread_main_thread_np()或者 [NSThread mainThread]来获取主线程,也可以通过pthread_self() [NSThread currentThread]来获取当前的线程。

    和runloop相关的有5个类,分别是
    NSDefaultRunLoopMode:app默认的mode,通常主线程是在这个mode下运行的
    UITrackingRunLoopMode:界面跟踪mode。用于scorllview追踪触摸滑动,保证界面滑动时不受影响
    UIInitializationRunLoop:刚启动时app进入第一个mode,启动完成就不再使用
    GSRunLoopReceiveRunLoopMode:接受系统时间的内部mode,绘图服务,通常用不到
    NSRunLoopCommonModes:这是一个占位用的mode,不是一种真的mode

    ARC

    OC为了处理对象,将变量类型定义为id类型和各种对象类型
    对象类型就是指向NSObject这样的OC指针(NSObject
    id类型用于隐藏对象类型的类名。相当于C语言中的void

    所有权修饰符

    所有权修饰符一种有4种
    __strong
    __weak
    __unsafe_unretained
    __autoreleasing

    __strong

    __strong是id类型和对象类型默认的所有权修饰符
    id object = [NSObject new]等价于 id __strong object = [NSObject new];

    强引用的变量,在超过变量作用域时,会自动释放。

    {
            id object = [NSObject new]
    }
    超过作用于自动释放
    

    __weak
    oc使用引用记数,因此会出现循环引用的问题。__weak就是为了解决循环引用的问题。
    下面的例子会出现循环引用

    id test0 = [Test new];
    id test1 = [Test new];
    test0.myObj = test1
    test1.myObj = test0
    

    持有某对象的若引用时,如果该对象被废弃,此弱引用自动失效,并被置为nil

    __unsafe_unretrained

    __unsafe_unretrained不会持有对象(类似弱引用),但是指向对象被释放时,不会自动指向nil,还是会指向原来的地址,所以会导致野指针。
    一般不使用__unsafe_unretrained

    __autoreleasing

    使用ARC的时候,不能直接使用autorelease方法,但是可以使用__autoreleaseing权限修饰符
    ARC无效的时候的写法

    NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init];
    id obj = [[NSObject alloc] init];
    [obj autorelease]
    [pool drain];
    

    ARC有效的时候的写法

    @autoreleasepool {
            id __autoreleasing obj = [[NSObject alloc] init];
    }
    
    

    编译器会自动检查方法名是否以alloc/new/copy/mutableCopy开始,如果不是则自动将返回值注册到autoreleasepool。

    ARC下的内存管理规则

    不能使用retain/release/retainCount/autorelease

    不能使用NSAllocateObject/NSDeallocateObject

    必须遵守内存管理的方法命名

    alloc
    new
    copy
    mutableCopy
    以上述名称开始的方法在返回对象时,必须返回给调用放应当持有的对象
    ARC追加了一条
    init方法必须是实例方法。返回的对象应当是id类型、方法声明的对象类型、或者是该类型的父类或子类

    不要显式调用dealloc

    使用@autoreleasepool代替NSAutoreleasePool

    不能使用区域(NSZone)

    对象类型变量不能作为C语言结构体成员

    void*和id之间转换,必须强制转换

    尽量不要使用这种转换方式,ARC不推荐
    ARC下面的三种转换方式

    id obj = [[NSObject alloc] init];
    void *p = (__bridge void*)obj;
    id o = (__bridge id)p
    这种转换类似于 __unsade_unretained。对象释放不会置为nil,所以很容易出现野指针
    
    id obj = [[NSObject alloc]init];
    void *p = (__bridge_retrained void*)obj
    对象引用记数+1,相当于retain
    相当于
    id obj = [[NSObject alloc] init];
    void *p = (__bridge_retained void*)obj
    
    id obj = (__bridge_transfer id)p
    相当于
    id obj = (id)p
    [obj retain];
    [(id)p release]
    

    属性

    属性和所有权修饰符对应
    assign __unsade_unretained
    copy __strong (赋值的是被复制的对象)
    retain __strong
    strong __strong
    unsafe_unretained __unsafe_unretained
    weak __weak

    copy不是简单的赋值,他的赋值是通过NSCopying接口的copyWithZone方法生成的对象,然后赋值。
    id *类型默认是autorelease,因此可以显式的使用_strong
    动态数组(id *)中使用操作符和静态数组(id[])不一样,需要手动释放内存。
    因为在静态数组中,编译器能够根据变量的作用域自动插入释放内存的代码。但是编译器不能确定数组的生命周期,所以动态数组需要按照下面的方式释放

    for (NSInteger i = 0; i < num; i++) {
            array[i] = nil; 
    }
    
    free(array)
    

    类的成员变量和属性

    类的成员变量

    @interface ViewController : UIViewController
    {
        //成员变量
        NSArray *array1;
    }
    

    类的属性

    @property (nonatomic,strong) NSArray *array2;
    

    类的属性会自动创建一个带下划线的成员变量,并且会给这个成员变量创建getter和setter方法。

    ARC的实现

    __strong的实现

    id __strong obj = [[NSObject alloc] init];
    

    编译器会自动插入释放代码,如下

    id obj = objc_msgSend(NSObject, @selector(alloc));
    objc_msgSend(obj, @selector(init));
    objc_release(obj)
    

    __weak的实现

    {
            id __weak obj1 = obj;
    }
    

    等价于

    id obj1
    objc_initWeak(&obj1, obj);
    objc_destoryWeak(&obj1);
    

    等价于

    id obj1;
    obj1 = 0;
    objc_storeWeak(&obj1, obj);
    objc_storeWeak(&obj1,0);
    

    oc会维护一张weak表,key是对象的地址,value是带有weak变量的地址。
    objc_storeWeak(&obj1, obj)把对象的地址作为key,变量的地址作为value存放到weak表里面。
    objc_storeWeak(&obj1,0);把对象对象地址对应的value清0

    废弃对象时,会根据该对象的地址,找到weak表里面对应的变量地址,然后给变量赋值nil。

    相关文章

      网友评论

        本文标题:OC基础知识

        本文链接:https://www.haomeiwen.com/subject/wgxdhltx.html