一、OC简介
Objective_C简称OC,是 C语言的超集(兼容 C 的代码)
OC是扩充自C语言的面向对象编程语言
面向对象编程:OOP (Object Oriented(面向) Programming)
面向对象特点包括:封装 、继承 、多态
面向对象 与 面向过程 :是两种不同的编程思想
类和对象
流程:定义类—>创建对象—>使用对象
类:具有相同特征或行为的事物的抽象
对象:就是类的实例(类是对象的类型)
1.定义类
类的定义:
①接口部分:对外声明类的特征和行为 // 接口部分和实现部分要分开写
标识: @interface…@end
包含内容:类名、父类名、实例变量、方法等
②实现部分:对内实现行为
标识:@implementation...@end
包含内容:实现方法 即实现类的行为
.h文件(接口文件或头文件)
@interface person/*类名*/ : NSObject/*父类名*/{
@public //实例变量(特征)
NSString *_name; //姓名
NSString *_sex; //性别
NSString *_hobby; //兴趣
int _age; //年龄
}
/************方法(行为部分)************/
- (void)sayHi;- (void)eat;
@end
.m文件(实现文件)
@implementation person//实现方法(实现行为)
- (void)sayHi{ NSLog(@"你好,我叫%@,今年%d岁,喜欢%@",_name,_age,_hobby);
}
- (void)eat{
NSLog(@"我要去吃饭");
}
@end
2.创建对象
①分配内存空间:根据类中声明的实例变量为对象分配内存,将所有实例变量置为默认值0,并返回首地址
person *p = [person alloc];
②初始化:为对象的实例变量设置初始值
p = [p init];
以上两步可写在一起: person *p = [[person alloc]init];
+(id)alloc; +号表示这个方法属于类,只能类执行。id返回值类型,表示任意类型的对象,即创建好的对象
-(id)init; -号表示这个方法属于对象,只能对象执行。id返回值类型,表示初始化完成的对象
子类可以重写父类的方法:
二、实例变量可见度和方法
实例变量的可见度:
@public(公有的) :实例变量可以在类的外部和内部操作
@protected(受保护的,默认的) : 实例变量只能在该类和其子类内操作
@private(私有的) : 实例对象只能在该类内访问
方法:
①类方法:只能类使用 例:+(id)alloc 注:类方法不能使用实例变量
②实例方法:只能对象使用,例如:-(void)sayHi
例:- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
包括:方法类型标示符,返回类型 ,参数形容词:参数类型,参数名
“ : ”标识参数不能省略。由冒号必须有参数
③方法使用:
[people sayHi]; 给people发送sayHi消息,或者使用点语法 people.sayHi;
1.setter:设置器(为实例变量赋值的方法)
如果一个实例变量是 int age 或者 _age.
setter书写格式:-(void)setAge:(int)age;(忽略下划线)
//传参时有几个冒号 ,就是传几个参数
2.getter:访问器(读取实例变量值的方法)
getter书写格式:-(int)age;(忽略下划线)
注意:无论是setter还是getter,内部操作都是实例变量,(每个实例变量都需要一对setter和getter方法)
3.自定义初始化方法:
-(id)initWithName:(NSString *)name sex:(NSString * )sex;
类的引用:
#import :导入头文件
#import “”导入自定义类
#import <>导入类库中的头文件
// 类似于C语言中的 #include 但是可以避免头文件重复导入
@class:
告诉编译器@class后的字符串作为类名使用,并未导入类的接口内容
有效避免嵌套循环导入
三、继承、初始化、遍历构造器
1.继承:父类 、子类
①继承是单向的,不可相互继承
②传递性:A继承于B,B继承于C,则A 同时具有 B和C的特征和行为
③子类可以继承父类全部的特征和行为(也可具备部分特征或行为)//@private标识的父类特征 不能被继承
④OC中只允许单继承(即每个类只能有一个父类)
⑤没有父类的类称为根类。OC中基类是NSObject(祖宗)
⑥继承的内容:所有实例变量和方法
⑦如果子类不满意父类方法的实现,可以重写父类的方法。
2.初始化方法
两步:①开辟空间alloc ②初始化init
作用:为某些实例变量赋初值
初始化方法在对象的整个生命周期里只是用一次
3.super 编译器指令,并非对象
作用:给super发消息,可以执行父类中实现的方法
子类可以重写父类的方法,即子类既有自己的实现,又有父类继承下来的实现,如果想使用父类的实现,向super发送消息
完整初始化方法:
@implementation Student
- (instancetype)init{
//给super发送init消息,即执行父类中实现的init方法
self = [super init];
if (self) {
//初始化设置
}
return self; //返回初始化完成的对象
}
//便利构造器:(+方法)
//封装了alloc和初始化方法,使用起来更加简洁
+(instancetype)girlFriendWithName:(NSString *)name gender:(NSString *)gender age:(NSInteger)age{
GirlFriend *g = [[GirlFriend alloc]initWithName:name gender:gender age:age];
return g;
}
四、字符串、数组
#pragma mark ------------- NSString -----------
#pragma mark赋值
NSString*s1 =@"123";
NSLog(@"s1 = %@",s1);
#pragma mark初始化方法
//一般初始化
NSString*s2 = [[NSString alloc]initWithFormat:@"%@今年%@岁",@"小明",@"18"];
NSLog(@"s2 = %@",s2);
//便利构造器初始化
NSString*s3 = [NSString stringWithString:s2];
NSLog(@"s3 = %@",s3);
NSString*s4 = [NSString stringWithFormat:@"%@你好",@"美女"];
NSLog(@"s4 = %@",s4);
#pragma mark通过文件路径获取文件中的字符串
NSString*s5 = [NSString stringWithContentsOfFile:@"/Users/王胜利/Objective_C/OC_课堂及作业/OCLesson4_课后/OCLesson4_课后/1.txt"encoding:NSUTF8StringEncodingerror:nil];
NSLog(@"s5 = %@",s5);
//usedEncoding :表示不知道编码方式 写nil
#pragma mark length
NSUIntegerui = [s5 length];
NSLog(@"lenthOfs5 is %lu",ui);
#pragma mark前后缀
BOOLb1 = [s4 hasPrefix:@"美女"];//判断前缀
BOOLb2 = [s4 hasSuffix:@"约么"];//判断后缀
NSLog(@"前缀%d,后缀%d",b1,b2);
#pragma mark大小写转换
NSString*s6 =[s5 capitalizedString];
NSLog(@"首字母大写:%@",s6);
NSString*s7 = [s6 uppercaseString];
NSLog(@"所有字母大写:%@",s7);
NSString*s8 = [s7 lowercaseString];
NSLog(@"所有字母小写:%@",s8);
#pragma mark比较字符串大小
NSComparisonResult b3 = [s6 compare:s7];
NSLog(@"compareResult = %ld",b3);
#pragma mark判断字符串是否相等
BOOLb4 = [s6 isEqualToString:s8];
#pragma mark 获取某个index的字符(遍历)
NSLog(@"%c",[s6 characterAtIndex:3]);
#pragma mark截取字符串
NSString*s9 = [s6 substringFromIndex:3];//截取index = 3往后的内容(包含index = 3)
NSString*s10 = [s6 substringToIndex:2];//截取到index = 2(不包含index = 2)
NSString*s11 = [s6 substringWithRange:NSMakeRange(5,4)];//从index = 5开始截取4个字符
#pragma mark ----------NSMutableString---------
//NSMutableString 继承于NSString 上面NSString方法NSMutableString同样适用
NSMutableString*mS1 = [[NSMutableString alloc]initWithFormat:@"abc123"];
#pragma mark改
[mS1 setString:@"ABC123"];
#pragma mark拼接
[mS1 appendString:@"def"];
#pragma mark 替换掉某一范围的字符串
[mS1 replaceCharactersInRange:NSMakeRange(3,3) withString:@"456"];
#pragma mark删除
[mS1 deleteCharactersInRange:NSMakeRange(3,3)];
#pragma mark插入
[mS1 insertString:@"hijk"atIndex:5];
#pragma mark ---------------NSArray----------------
#pragma mark初始化方法
NSArray*a1 = [[NSArray alloc]initWithObjects:@"1",@"2",@"3",nil];
//便利构造器
NSArray*a2 = [NSArray arrayWithObjects:@"4",@"5",@"6",nil];
NSArray*a3 = [NSArray arrayWithArray:a2];
//字面量(语法糖)
NSArray*a4 =@[@"a",@"b",@"c"];
#pragma mark获取元素个数
NSUInteger ui1 = [a1 count];
#pragma mark取出元素下标
NSUInteger ui2 = [a4 indexOfObject:@"b"];
#pragma mark根据下标取出下标所有的元素
NSString*s = [NSString stringWithString:[a4 objectAtIndex:2]];
#pragma mark -----------NSMutableArray-----------
NSMutableArray*mA =[NSMutableArray arrayWithArray:a1];
#pragma mark 增加元素
[mAaddObject:@"4"];
#pragma mark 删除元素
[mA removeLastObject];
[mA removeObjectAtIndex:2];
#pragma mark插入元素
[mAinsertObject:@"3"atIndex:2];
#pragma mark替换
[mA replaceObjectAtIndex:2withObject:@"a"];
#pragma mark -------------NSNumber-----------
inti =2;
NSNumber*_i = [NSNumber numberWithInt:i];//将int基本数据类型转换成对象类型
[_i stringValue];//将对象类型 转换成 字符串
inta = [_i intValue];//将对象类型转换成基本数据类型
五、 字典、集合
1 .NSDictionory字典
定义:字典是用于保存具有映射关系(key - value对)数据的集合
特征:
①key- value 在 dictionary中认为是一个条目(Entry)
②key不能重复,value必须是对象
③键值对在字典中是无序存储的
NSDictionary不可变字典:
字典一旦创建,键值对就无法改变,不可添加,不可删除, 只读
2.NSMutableDictionary可变字典:
可以对字典进行 <增><删><改> 操作
3.NSSet集合
特征:
①集合中元素唯一
②存储的数据是无序存储
③存储元素必须是对象类型
NSSet:
NSMutableSet:
NSCountedSet:
- 集合类型的快速枚举
快速枚举:
for(<#type*object#> in <#collection#>){ }
①object是遍历得到的元素对象
②collection是集合类型的对象:数组,字典,集合.
特点:
①数组枚举得到数组中的元素对象
②字典枚举得到字典中的key值
③集合枚举得到集合中的元素对象
2.数组排序
- (NSArray *)sortedArrayUsingSelector:(SEL)comparator;
- (void)sortUsingSelector:(SEL)comparator;
六、 Block
代码块本质上是和其他变量类似。不同的是,代码块存储的数据是一个函数体。使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值。
脱字符(^)是块的语法标记。按照我们熟悉的参数语法规约所定义的返回值以及块的主体(也就是可以执行的代码)。下图是如何把块变量赋值给一个变量的语法讲解:
按照调用函数的方式调用块对象变量就可以了:int result = myBlock(4); // result是 28
1.参数是NSString*的代码块
void (^printBlock)(NSString *x);
printBlock = ^(NSString* str) {
NSLog(@"print:%@", str);
};
printBlock(@"hello world!");
运行结果是:print:hello world!
2、代码用在字符串数组排序
NSArray *stringArray = [NSArray arrayWithObjects:@"abc 1", @"abc 21", @"abc 12",@"abc 13",@"abc 05",nil];
NSComparator sortBlock = ^(id string1, id string2) {
return [string1 compare:string2];
};
NSArray *sortArray = [stringArray sortedArrayUsingComparator:sortBlock];
NSLog(@"sortArray:%@", sortArray);
运行结果:sortArray:(
** "abc 05",**
** "abc 1",**
** "abc 12",**
** "abc 13",**
** "abc 21"**
)
3、代码块的递归调用
代码块想要递归调用,代码块变量必须是全局变量或者是静态变量,这样在程序启动的时候代码块变量就初始化了,可以递归调用
static void (^ const blocks)(int) = ^(int i) {
if (i > 0) {
NSLog(@"num:%d", i);
blocks(i - 1);
}
};
blocks(3);
运行打印结果:
num:3
num:2
num:1
4、在代码块中使用局部变量和全局变量
在代码块中可以使用和改变全局变量
int global = 1000;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^block)(void) = ^(void) {
global++;
NSLog(@"global:%d", global);
};
block();
NSLog(@"global:%d", global);
}
return 0;
}
运行打印结果:
global:1001
而局部变量可以使用,但是不能改变。
int local = 500;
void(^block)(void) = ^(void) {
local++;
NSLog(@"local:%d", local);
};
block();
NSLog(@"local:%d", local);
在代码块中改变局部变量编译不通过。怎么在代码块中改变局部变量呢?在局部变量前面加上关键字:__block
__block int local = 500;
void(^block)(void) = ^(void) {
local++;
NSLog(@"local:%d", local);
};
block();
NSLog(@"local:%d", local);
运行结果:local:501
七、 时间日期
1. NSDate
NSDate *date = [NSDate date];//初始化 获取0时区现在日期时间
NSTimeZone *zone = [[NSTimeZone alloc]init]; zone = [NSTimeZone localTimeZone];//获取本地时区
NSInteger offest = [zone secondsFromGMT];//获取本地时区和0时区的时间差 (以 分钟 算)
NSTimeInterval subTime = [date1 timeintervalSinceDate: date2];//计算两个日期的时间差
2. NSDateFormatter
//作用:iOS中的时间日期格式类 用于实现NSString和NSDate的互相转化
NSDateFormatter *formatter = [[NSDateFormatteralloc]init];
[formatter setDateFormat:@"yyyy年MM月dd日 HH点mm分ss秒"];
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:8]];
// 1.将日期转换成字符串输出
NSString *date1 = [formatter stringFromDate:[NSDate date]];
NSLog(@"%@",date1);
NSString *s = @"2014年05月01日 10点23分18秒";
// 2.将字符串转换为日期
NSDate *date2 = [formatter dateFromString:s];
NSLog(@"%@",date2);
苹果系统内部,时间是以零时区为基准存储
默认情况下
stringFormDate:(把日期转换成字符串显示) 系统会自动 算 零时区时间加上 时区差(GTM) 的时间显示
dateFromString:(把时间字符串转换成日期) 系统存储零时区时间,,默认会把我们输入的时间减去 时区差(GTM)存储在系统中
使用 NSDateFormatter 时 格式化格式 还有时区
3. Category 类目
- 为没有源代码的类添加方法
- 添加的类会成为原类的一部分 可以被继承
4. Extension 类的延展
为自定义类定义"私有的”方法 外部不可访问
两种定义方法:
- 定义一个 Extension 文件 只有.h文件 在里面添加 方法
- 在需要的类的.m 文件上面 添加 @Interface 类名() 方法名 @end
5. Protocol 协议
就是一套标准(一堆方法声明),,只有.h文件
在接受协议的对象的类的.m中实现协议中定义的方法
关键字:
@required 默认的,方法 代理必须实现
@optional 可以选择实现,,也可以不实现
遵守协议步骤:
- 在类的.h文件 父类名后写上<协议名>
- 在.m文件中实现协议中的方法
delegate 代理
Protocol 的核心使用场景是实现delegate设计模式
八、属性
1.属性 @property
为实例变量提供了setter getter 方法的默认实现
.h文件中 @property 声明了两个方法 (setter getter)
.m文件中 使用@synthesize实现属性
注意:使用@property声明属性 可以省略 @synthesize和实例变量
2.属性的属性 attribute
//1.读写设置
//读写性控制:readwrite 读写 (默认设置) 即声明setter 又声明getter
@property(readwrite) NSString *name;
//readonly 只读 只声明getter方法
@property(readonly) NSString *sex;
//setter 给getter重命名
@property(getter= fun1)NSString * address;
//getter 给setter重命名
/*自定义方法名后面加冒号 (setter有参数)*/
@property(setter=setPhoneNum:)NSString *phoneNum;
//2.原子能设置
//原子性控制atomic (默认设置) setter getter内部做了多线程访问处理,多线程访问下比较安全 (缺点:暂用系统线程资源,单线程下不建议适用)
@property(atomic)NSString *hobby;
//nonatomic : setter getter内部没有做多线程访问处理,,(程序通常使用nonatomic,只有当属性需要线程安全是,才定义 atomic)
@property (nonatomic)NSString *job;
//3.类型设置
//语义设置assign :(在setter getter 内部直接复制)当属性是非对象类型(int float 等基本数据类型)时使用
@property (nonatomic ,assign)NSInteger age1;
//retain : setter getter 内部会做内存优化 (当属性是对象时使用)
@property (nonatomic ,retain)NSString *name1;
//copy : 同 retain 一样,setter getter内部会做内存优化,,,但是copy遵循(NSCopying协议) 当属性是对象类型,并且想要得到参数时,使用copy 关键字
@property(nonatomic ,copy)NSString *address1;
// 4.点语法(配合setter getter 或者属性一起使用)
SingleDog *sdog1 = [[SingleDog alloc]init]; [sdog1 setName:@"zhangsan"];
[sdog1 name];
// 直接使用点语法赋值 (只要有setter getter方法)
sdog1.name = @"lisi"; NSLog(@"%@",sdog1.name); sdog1.gender = @"nan";
NSLog(@"%@",sdog1.gender);
//5.KVC(Key - Value - Coding) 键值编码
是一种间接访问实例变量的方法
key : 键 (用于标识实例变量)
value : 实例变量的值
①//使用KVC给实例变量赋值
//类似于setter方法
[sdog1 setValue:@"wangwu" forKey:@"name"];
//获取值
//类似于getter方法
NSLog(@"%@",[sdog1 valueForKey:@"name"]);
//②KVC可以给私有变量(@private修饰的实例变量)赋值和取值 (key不能写错,字符串匹配)
[sdog1 setValue:@"dasfafa" forKey:@"phoneNum"];
//③ 当key不存在的时候,系统会执行setValue:forUndefinedKey:
//然后抛出一个异常,但是 该方法只与 setValue:ForKey搭配使用
//注意:一般从网络上请求下来的数据,一般都是数组套字典,字典套数组之类的,直接用KVC去取值赋值这时候一定要加上 setValue :forUndefinedKey这个方法(防止崩溃)
//④setValue:forKeyPath使用点语法 ,,引用路径 设置或输出值
Pig *p1 = [[Pigalloc]init];
Food *f1 = [[Foodalloc ]init];
[p1 setValue:f1 forKey:@"food"];
[p1 setValue:@"白菜" forKeyPath:@"[food.name](http://food.name/)"];
NSLog(@"%@",[p1 valueForKeyPath:@"[food.name](http://food.name/)"]);
NSLog(@"%@",[f1 valueForKey:@"name"]);
//⑤setValue:ForKeysWithDictionary借用字典同时设置读取多个key 和 value
NSMutableDictionary *dic1 = [NSMutableDictionarydictionary];
NSDictionary *dic2 =[NSDictionary dictionaryWithObjectsAndKeys:@"zhaoda",@"name",@"nan",@"gender", nil];
[dic1 setValuesForKeysWithDictionary:dic2];
NSLog(@"%@",dic1);
[sdog1 setValuesForKeysWithDictionary:dic2];
NSLog(@"%@ %@",sdog1.name,sdog1.gender);
九、内存管理初级
iOS应用出现 Crash(闪退) ,90%原因是因为内存问题
//主要有 :内存溢出 野指针异常 过早释放等
//垃圾回收(gc):java 里的内存管理机制(程序员只负责开辟,由系统自己判断哪些空间不再使用并收回)
OC中的两种内存管理方式:
MRC:(Manual Reference Count)手动引用计数
内存的开辟和释放都是有程序代码进行控制.(对程序员要求较高)
ARC:(Auto Reference Count)自动引用计数
iOS 5 之后新加的编译器特性,程序员只负责开辟空间,不用去释放
// ARC的本质还是MRC,只是编译器加了自动释放的代码
C语言中
:使用malloc和free 进行堆内存的创建和释放,,堆内存只有使用和销毁两种状态
OC中
:引用引用计数机制来管理内存(多个指针同时放问一款内存)当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减.当引用计数到零时,该对象就将释放占有的资源
影响引用计数的方法:
1.alloc:开辟内存空间 引用计数从0到1
Person *p1 = [[Person alloc]init];
2.retain:引用计数 + 1
Person*p2 = [p1 retain];
//实际开发中不用
//注意:当使用retain是若不找另一个指针子向它,则会出现内存泄露问题
retainCount方法:获取当前对象的引用计数.
NSLog(@"%lu",[p1 retainCount]);
//注意:1.实际开发中 retainCount不要用 2.即使要用也只能用在自定义的类上
3.copy :将某一内存区域的内容拷贝一份,拷贝到新的内存空间中,(被拷贝的引用计数不变,新的内存区域引用计数为1)
4.release:引用计数 -1
//当一个对象用完之后,要用 release 或者 autoRelease 去释放
//release 到0之后不能再继续 release 过度释放也会崩溃
5.dealloc:销毁对象
他是继承自父类的方法,当引用计数为0的时候,由对象自动调用
//一般我们要在类的.m文件中重写一下dealloc方法,,把我们声明的属性也销毁
- (void)dealloc{
//[_age release];//assign
修饰非对象类型,,没有对引用计数产生影响,,,无需释放
[_name release];
//对象销毁了,
属性没有用,也要销毁
[_gender release];
NSLog(@"%@被销毁",self);
[super dealloc];
}
6.autorelease:在未来的某一时刻引用计数 -1.
autorelease对象的释放收 autoreleasepool 的控制
NSAutoreleasePool*pool = [[NSAutoreleasePool alloc]init];
Person*p6 = [[Person alloc]init];
[p6 retain];
[p6 autorelease];
NSLog(@"++++%lu",[p6 retainCount]);
[pool release];
NSLog(@"====%lu",[p6retainCount]);
// 从开辟空间初始化autoreleaepool对象,到pool对象被release 就相当于@autoreleasepool加上{}
//但自iOS 5之后推荐使用 @autoreleasepool { }
//@autoreleasepool { }出了大括号,自动释放池才向各个对象发送release消息
//如果使用了@autoreleasepool{} ,,,自动释放池是一种栈的结构
总之,凡是使用了alloc retain copy 让内存的引用计数增加了,就需要使用release或者autorelease让内存的引用计数减少,在一段代码内,增加和减少的次数要相等
copy方法 :
**一个对象想要copy,生成自己的副本,需要遵守NSCopying协议,定义copy的细节,,(如果类没有接受NSCopying协议而给对象发送copy消息,会引起carsh)**
.h文件
@interfacePerson :NSObject<NSCopying>//手动添加遵守NSCopying协议
@property(nonatomic,copy)NSString*name;
@end
.m文件
@implementation Person
- (void)dealloc{
[_name release];
NSLog(@"销毁");
[super dealloc];
}
//- (id)copyWithZone:(NSZone *)zone{
// //[self retain]把引用计数+1
// return [self retain];
// //浅拷贝相当于只拷贝了指针 (retain只不过它遵循了NSCopying协议)
//}
- (id)copyWithZone:(NSZone*)zone{
Person*p = [[ Person alloc]init];
p.name=self.name;
return p;
//深拷贝新建了一个对象,并将对象return出去
//真正意义上得拷贝,他开辟了一个新的空间对象也return了出去
}
@end
main.m文件
Person*p1 = [[Person alloc]init];
Person*p2 = [p1 copy];
NSLog(@"%p",p1);
NSLog(@"%p",p2);
NSLog(@"%lu",[p1 retainCount]);
NSLog(@"%lu",[p2 retainCount]);
打印结果
十、内存管理高级
属性内部实现
assign:
- (NSInteger)age{
return _age;
}
- (void)setAge:(NSInteger)age{
_age = age;
}
retain:
- (NSString *)name{
return [[_name retain] autorelease];
}
- (void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
copy:
- (NSString *)gender{
return [[_gender retain]autorelease];
}
- (void)setGender:(NSString *)gender{
if (_gender != gender) {
[_gender release];
_gender = [gender copy];
}
}
delloc://由系统自动调用,,永远不要自己手动调用
- (void)dealloc{
[_name release];
[_gender release];
[super dealloc];//最后一定要调用父类的delloc方法
}
便利构造器的内存管理
+(instancetype)personWithName:(NSString *)name Age:(NSInteger)age Gender:(NSString *)gender{
Person *p = [[Person alloc]initWithName:name Age:age Gender:gender];
// [p release];//release 写在上面,下面return是,,p就成了野指针
// return p;
// [p ralease];//release写在下面,,会出现内存泄露
return [p autorelease];
}
//如果是便利构造器声明的对象,无需释放,因为便利构造器内部谢了autorelease方法,,出了花括号自动释放
Collection(NSArray等容器类)的内存管理
- 加入collection的对象会被retain
- 移除collection的对象会被release
- collection被释放时,会对内部所有对象release
多态:
父类指针可以指向子类对象