copy和mutableCopy

copy和mutableCopy
一、一个对象使用copy或mutableCopy方法能够建立对象的副本
二、copy - 须要先实现NSCopying协议,建立的是不可变得副本(如NSString、NSArray、NSDictionary)
三、mutableCopy - 须要先实现NSMutableCopying协议,,建立的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
四、深复制:内容拷贝,源对象和副本指向不一样的两个对象。源对象引用计时器不变,副本计数器设置为1
五、浅复制:指针拷贝,源对象和副本指向的是同一个对象。对象的引用计数器+1,其实至关于作了一次retain操做
六、只有不可变对象建立不可变副本(copy)才是浅复制,其余都是深复制
copy语法的目的:改变副本的时候,不会影响到源对象

为自定义类添加复制功能
一、若是想自定义copy,那么就必须遵照NSCopying,而且实现copyWithZone方法
二、若是想自定义mutableCopy,那么就必须遵照NSMutableCopying,而且实现mutableCopyWithZone:方法
三、以copy为例,建议用[self class]代替直接类名app

- (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类型的对象

获取内存中的类对象:ui

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的类方法:atom

+ ( void )texst;


Person.m中有实现该方法:spa

+ ( void )test {
     NSLog(@ "调用了类方法test" );
}


测试:指针

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


类的加载和初始化:
+load
一、在程序启动的时候会加载全部的类和分类,并调用全部类和分类的+load方法
二、先加载父类,再加载子类:也就是先调用父类的+load方法,再调用子类的+load方法
三、先加载原始类,再加载分类
四、无论程序运行过程有没有用到这个类,都会调用+load加载

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

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

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

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

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

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


父类的属性交给父类方法去处理,子类方法处理子类本身的属性
自定义构造方法的规范:
一、必定是对象方法,必定以"-"开头
二、返回值通常是id类型
三、方法名通常以init开头

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

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

ARC
ARC的判断准则:只要没有强指针指向对象,就会释放对象
ARC的特色:
一、不容许调用release、retain、retainCount
二、容许重写dealloc,可是不容许调用[super deallo]
三、@property的参数:
    * strong:成员变量时强指针,至关于原来的retain(适用于OC对象类型)
    * weak:成员变量时弱指针,至关于原来的assign(适用于OC对象类型)
    * assign:适用于非OC对象类型

指针分两种:
一、强指针:默认状况下,全部的指针都是强指针 __strong
二、弱指针:__weak

Xcode是默认使用ARC的,若是某个.m文件真的不想使用ARC,能够经过如下步骤来不适用ARC:
选择Xcode右侧项目树的根,而后是TARGETS -> Build Phases -> Compile Sources,下拉,选择目标.m文件,回车或者双击,弹出输入框,输入"-fno-objc-arc"回车,就能够了,以下图所示:

5486fd6b0001924105000173.jpg

 

若是开发环境是非ARC的,想要使用ARC的,将上面的"-fno-objc-arc"改为"-f-objc-arc"就能够了。

ARC循环引用
当两端循环引用的时候,解决方案以下:
一、ARC
    1端用strong,另外一端用weak
二、非ARC
    1端用retain,另外一端用assign
例如:
在使用ARC下,有两个类:Person、Dog,以下:
Person.h:

@ class  Dog;
 
@interface Person : NSObject
// 人有一只狗,strong,强指针
@property (nonatomic, strong) Dog *dog;
@end


Person.m:

@implementation Person
- ( void )dealloc {
     NSLog(@ "Person---- dealloc" );
}
@end


Dog.h:

@ class  Dog;
 
@interface Dog: NSObject
// 狗有一个主人,strong,强指针
@property (nonatomic, strong) Person *person;
@end
 
Dog.m:
@implementation Dog
- ( void )dealloc {
     NSLog(@ "Dog ---- dealloc" );
}
@end


main.m:

Person *p = [[Person alloc] init];
Dog *d = [[Dog alloc] init];
// 第一种状况:
// 当两个互指以后,会出现内存泄露,两个对象的dealloc没有被调用,也就是两个对象的内存没有被释放
p.dog = d;
d.person = p;

如图所示:

5486fd7d000140a105000379.jpg

// 第二种状况:
// 若是注释上面两行代码中的任意一行,两个对象均可以被释放
// 由于当main函数执行完毕以后,对象p和d都被回收,可是两个内存中的对象有强指针,不会被回收,因此会形成内存泄露
p.dog = d;
// d.person = p;

如图所示:

5486fd860001834a02690435.jpg

 

若是将Dog中的person属性改为weak:

@property (nonatomic, weak) Person *person;

那么,上面第二种状况就变成了以下图所示:

5486fd940001854a02620433.jpg

 

这样的话,当程序运行结束,被回收的就是Person对象,既然Person对象被回收了,那么Dog对象就没有了强指针,也会被回收了。

相关文章
相关标签/搜索