• copy和mutableCopy



    copy和mutableCopy

    1、一个对象使用copy或mutableCopy方法可以创建对象的副本
    2、copy - 需要先实现NSCopying协议,创建的是不可变得副本(如NSString、NSArray、NSDictionary)
    3、mutableCopy - 需要先实现NSMutableCopying协议,,创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
    4、深复制:内容拷贝,源对象和副本指向不同的两个对象。源对象引用计时器不变,副本计数器设置为1
    5、浅复制:指针拷贝,源对象和副本指向的是同一个对象。对象的引用计数器+1,其实相当于做了一次retain操作
    6、只有不可变对象创建不可变副本(copy)才是浅复制,其他都是深复制
    copy语法的目的:改变副本的时候,不会影响到源对象


    为自定义类添加复制功能
    1、如果想自定义copy,那么就必须遵守NSCopying,并且实现copyWithZone方法
    2、如果想自定义mutableCopy,那么就必须遵守NSMutableCopying,并且实现mutableCopyWithZone:方法
    3、以copy为例,建议用[self class]代替直接类名

    - (id)copyWithZone:(NSZone *)zone {
    id copy = [[[self class] allocWithZone:zone] init];
    // 做一些属性的初始化
    return copy;
    }
    
    // 演示字符串的拷贝
    NSString *string = [[NSString alloc] initWithFormat:@"age is %i", 21];// 1
    // 产生了一个新的对象,引用计数器为1.源对象的引用计数器不变
    NSMutableString *str = [string mutableCopy];// 1
    NSLog(@"str: &zi", [str retainCount]); // 1
    NSLog(@"string: %zi", [string retainCount]);// 1
    // str和string不是相同对象
    NSLog(@"%i", str == string);
    // 修改str看string是否有被修改
    [str appendString@"123456"];
    NSLog(@"str = %@", str);
    NSLog(@"string = %@", string);
    [str release];// 0
    [string release];// 0
    // 如果一个对象是不可变的,copy的对象也是不可变的,系统会直接返回被copy的对象本身
    // copy产生的是不可变副本,由于源对象本身就不可变,所以为了性能着想,copy会直接返回源对象本身,相当于源对象的retain操作,引用计数器+1
    NSString *s1 = [[NSString alloc] initWithFormat:@"age is %i", 21];
    NSLog(@"s1: %zi", [s1 retainCount]);// 1
    NSString *s2 = [s1 copy];
    NSLog(@"s1: %zi", [s1 retainCount]);// 2
    NSLog(@"%i", s2 == s1);
    
    Student.h:
    
    @interface Student : NSObject <NSCopying>
    // copy代表setter方法会release旧对象,copy新对象
    // 修改外面的变量,并不会影响到内部的成员变量
    // 建议:NSString一般用copy策略,其他对象一般用retain
    @property (nonatomic, cop)NSString *name;
    
    + (id)studentWithName:(NSString *)name;
    @end
    
    Student.m:
    
    @implementation Student
    + (id)studentWithName:(NSString *)name {
    Student *stu = [[[Student alloc] init] autorelease];
    stu.name = name;
    return stu;
    }
    // 这里创建的副本对象不要求释放
    - (id)copyWithZone:(NSZone *)zone {
    Student *copy = [[Student allocWithZone:zone] init];
    copy.name = self.name;
    return copy;
    }
    
    - (void)description {
    return [NSString stringWithFormat:@"[name = %@]", _name];
    }
    
    - (void)dealloc {
    [_name release];
    [super release];
    }
    @end

    main.m:

    // 这里写self class是为了防止子类在创建的过程中导致类型错误
    Student *stu = [[[[self class] alloc] init] autorelease];
    NSMutableString *string = [NSMutableString stringWithFormat@"age is %i", 21];
    stu.name = string;
    [string addendString:@"abcd"];
    // 如果Student.h中的*name是retain,那么修改了string,stu.name也被修改了
    // 如果Student.h中的*name是copy,那么修改了string,stu.name就不会被修改
    NSLog(@"name = %@", stu.name);
    NSLog(@"string = %@", string);
    
    // Student的copy
    Student *stu1 = [Student studentWithName:@"student1"];
    // 如果Student没有实现NSCopying协议,那么会报错:unrecognized selector sent to instance....
    Student *stu2 = [stu1 copy];
    stu2.name = @"stu2";
    NSLog(@"stu1 = %@", stu1);
    NSLog(@"stu2 = %@", stu2);
    [stu2 release];

    类的本质
    类本身也是一个对象,是一个Class类型的对象,简称类对象
    Class类型的定义:
    typedef struct objc_class *Class;
    类名就代表着类对象,每个类只有一个类对象
    例如:
    利用Class创建Person类对象
    利用Person类对象创建Person类型的对象

    获取内存中的类对象:

    Person *p1 = [[Person alloc] init];
    Person *p2 = [[Person alloc] init];
    // 第一种方法
    Class c1 = [p1 class];
    Class c2 = [p2 class];
    // 第二种方法
    Class c3 = [Person class];
    NSLog(@"c1 = %p, c2 = %p, c3 = %p", c1, c2, c3);

    获取的类对象(Class)可以调用类方法,比如Person.h中有一个名为test的类方法:

    + (void)texst;
    
    Person.m中有实现该方法:
    
    + (void)test {
    NSLog(@"调用了类方法test");
    }

    测试:

    Class c = [p1 class];
    [c test];

    类的加载和初始化:
    +load
    1、在程序启动的时候会加载所有的类和分类,并调用所有类和分类的+load方法
    2、先加载父类,再加载子类:也就是先调用父类的+load方法,再调用子类的+load方法
    3、先加载原始类,再加载分类
    4、不管程序运行过程有没有用到这个类,都会调用+load加载

    +initialize
    1、在第一次使用某个类时(比如创建对象等),就会调用一次+initialize方法
    2、一个类只会调用一次+initialize方法,先调用父类的,再调用子类的

    + (void)load方法在程序启动的时候,就会加载一次项目中所有的类和分类。类加载完毕后就会调用每个类和分类的load方法。只会调用一次
    load方法会从父类开始调用,再是子类,包括Category也会调用load方法
    + (void)initialize方法在第一次使用某个类的时候调用
    initialize方法也是先初始化父类,再是子类

    description方法
    1、-description方法:使用NSLog和%@输出某个对象时,会调用对象的-description方法,并拿到返回值进行输出
    2、+description方法:使用NSLog和%@输出某个对象时,会调用类对象的+description方法,并拿到返回值进行输

    死循环陷阱:如果在-description方法中使用NSLog打印self

    构造方法
    作用:用来初始化对象的方法,是一个对象方法,"-"开头
    重写构造方法的目的:为了让对象创建出来,成员变量就已经有一些固定的值
    重写构造方法的注意点:
    1、先调用父类的构造方法([super init])
    2、再进行子类的内部成员变量的初始化
    例如:
    重写-init方法:

    - (id)init {
    // 1. 一定要调用回super的init方法:初始化父类中声明的一些成员变量和其他属性
    self = [super init];// 当前对象 self
    // 2. 如果对象初始化成功,才有必要进行接下来的初始化操作
    if (self != nil) {
    // 初始化成功
    _age = 10;
    }
    // 3. 返回一个已经初始化完毕的对象
    return self;
    }

    父类的属性交给父类方法去处理,子类方法处理子类自己的属性
    自定义构造方法的规范:
    1、一定是对象方法,一定以"-"开头
    2、返回值一般是id类型
    3、方法名一般以init开头

    NSLog输出的一些补充(都是两个下划线"_"):

    // 输出当前函数名
    NSLog(@"%s ", __func__);
    // 输出行号
    NSLog(@"%d", __LINE__);
    // NSLog输出C语言字符串的时候,不能有中文,可以使用printf函数输出
    // NSLog(@"%s", __FILE__);
    printf("%s ", __FILE__);
    2014-12-08


    ARC
    ARC的判断准则:只要没有强指针指向对象,就会释放对象
    ARC的特点:
    1、不允许调用release、retain、retainCount
    2、允许重写dealloc,但是不允许调用[super deallo]
    3、@property的参数:
    * strong:成员变量时强指针,相当于原来的retain(适用于OC对象类型)
    * weak:成员变量时弱指针,相当于原来的assign(适用于OC对象类型)
    * assign:适用于非OC对象类型

    指针分两种:
    1、强指针:默认情况下,所有的指针都是强指针 __strong
    2、弱指针:__weak

  • 相关阅读:
    mybatis 插件的原理-责任链和动态代理的体现
    优雅的对象转换解决方案-MapStruct使用进阶(二)
    将博客搬至CSDN
    python headers missing
    Gvim:unable to load python
    gvim keil 快捷跳转至出现错误(警告)行
    stm32 堆溢出
    keil在线烧录突然提示 No target connected #
    cygwin vim can't write .viminfo
    切换用户后,/etc/profile的配置不起效
  • 原文地址:https://www.cnblogs.com/xujinzhong/p/11155094.html
Copyright © 2020-2023  润新知