练习与示例:
1.代理
- 新建Boss类,在.h中声明其方法和代理属性:
@protocol NannyDelegate <NSObject>
- (void)washClothes;
- (void)cook;
@optional
- (void)takeCareBaby;
@end
@interface Boss : NSObject
//实现了这个协议的任意类型的对象
// 代理属性要用assign和weak去修饰,不需要使用oc中的内存管理
@property (nonatomic, assign)id<NannyDelegate>delegate;
-(void)clothesDirty;
-(void)hungry;
-(void)working;
@end
- 在.m中实现方法:
-(void)clothesDirty {
NSLog(@"衣服脏了");
[self.delegate washClothes];
}
-(void)hungry {
NSLog(@"饿了");
[self.delegate cook];
}
-(void)working {
NSLog(@"去上班");
// 判断我的代理人能否响应(是否实现)某方法
if ([self.delegate respondsToSelector:@selector(takeCareBaby)]) {
[self.delegate takeCareBaby];
} else {
NSLog(@"带孩子去上班");
}
}
- 新建一个Worker类,导入头文件并继承Boss类的代理:
#import "Boss.h"
@interface Worker : NSObject<NannyDelegate>
- 在.h中实现:
- (void)washClothes {
NSLog(@"洗衣服");
}
//- (void)cook {
// NSLog(@"做饭");
//}
- (void)cook {
NSLog(@"zuofan");
}
- (void)takeCareBaby {
NSLog(@"保姆照顾孩子");
}
- 在主函数中导入头文件:
#import "Boss.h"
#import "Worker.h"
- 在主函数中实现:
Boss *boss = [[Boss alloc]init];
Worker *worker = [[Worker alloc]init];
boss.delegate = worker;
[boss hungry];
[boss clothesDirty];
[boss working];
2.类的扩展
- 新建OC文件SayHi为NSString类,在.h中进行方法的声明:
- (void)sayHi;
- (void)getCharacterOfPre;
// 为NSString类扩展一个新的方法,要求获取字符串的首字母并且将该首字母大写。
- (NSString *)firstCharacter;
- 在.m中进行实现:
- (void)sayHi {
NSLog(@"我是%@,我是一个牛逼的字符串", self);//self指的是当前调用这个方法的字符串对象
}
- (void)getCharacterOfPre {
NSString *s = [[self substringToIndex:1] capitalizedString];
NSLog(@"%@", s);
}
- (NSString *)firstCharacter {
if (self.length > 0) {
return [[self substringToIndex:1] uppercaseString];
}
NSLog(@"空字符串别凑热闹");
return @"";//或者写nil或self
}
- 在主函数中进行头文件导入:
#import "NSString+SayHi.h"
- 在主函数中进行调用:
int main(int argc, const char * argv[]) {
[@"徐博杰" sayHi];
[@"dfdf" getCharacterOfPre];
NSLog(@"%@", [@"abc" firstCharacter]);
return 0;
}
- 新建一个Person类,在.m中自定义初始化方法:
@interface Person ()
@property(nonatomic, retain) NSString *age;
@end
@implementation Person
- (instancetype)init
{
self = [super init];
if (self) {
//私有方法通常是通过其它方法间接调用,目的是将某一部分的代码分装起来,多次使用
[self sayHi];
}
return self;
}
// 没在.h中声明的都是私有方法
- (void)sayHi {
NSLog(@"hahahaha");
}
@end
3.省市区
NSMutableArray *provinceArray = [NSMutableArray array];
// 加载文件得到字符串
// 参数一:文件路径
// 参数二:编码格式
// 参数三:错误信息
NSString *contentString = [NSString stringWithContentsOfFile:@"area.txt" encoding:NSUTF8StringEncoding error:nil];
// NSLog(@"%@", contentString);
// 以换行符分割字符串,得到每一行的字符串内容
NSArray *lineArray = [contentString componentsSeparatedByString:@"\n"];
for (NSString *lineString in lineArray) {
// NSLog(@"line:%@", lineString);
if (![lineString hasPrefix:@" "]) {
// 前缀不是空格是省名
// 创建一个省份字典
NSMutableDictionary *provinceDic = [NSMutableDictionary dictionary];
// 省份字典添加键值对(provinceName:省份名)
[provinceDic setObject:lineString forKey:@"provinceName"];
// 创建一个城市数组(用于后面存放城市字典)
NSMutableArray *cityArray = [NSMutableArray array];
// 添加键值对(cityName:城市名)
[provinceDic setObject:cityArray forKey:@"cityArray"];
[provinceArray addObject:provinceDic];
} else if (![lineString hasPrefix:@" "]) {
// 前缀有空格且不是四个空格为城市名
// 读取到城市名的时候需要找到该城市所属省份(当前省份数组中最后一个元素就是当前城市所属省份)
NSMutableDictionary *provinceDic = [provinceArray lastObject];
NSMutableArray *cityArray = [provinceDic objectForKey:@"cityArray"];
// 创建城市字典
NSMutableDictionary *cityDic = [NSMutableDictionary dictionary];
// 添加键值对(cityName:城市名)
[cityDic setObject:lineString forKey:@"cityName"];
//创建地区数组(后面用于存放地区名称)
NSMutableArray *areaArray = [NSMutableArray array];
// 添加键值对(areaArray:地区数组)
[cityDic setObject:areaArray forKey:@"areaArray"];
//将城市的字典存到对应的城市数组中
[cityArray addObject:cityDic];
} else {
// 前缀有空格且有四个空格的为地区名
// 获取当前地区所在省份
NSMutableDictionary *provinceDic = [provinceArray lastObject];
// 获取该省份的城市数组
NSMutableArray *cityArray = [provinceDic objectForKey:@"cityArray"];
// 从城市数组中获取当前城市所属城市
NSMutableDictionary *cityDic = [cityArray lastObject];
// 获取该城市的地区数组
NSMutableArray *areaArray = [cityDic objectForKey:@"areaArray"];
// 将地区名存到该地区数组
[areaArray addObject:lineString];
}
}
for (NSMutableDictionary *provinceDic in provinceArray) {
NSLog(@"省份:%@", [provinceDic objectForKey:@"provinceName"]);
for (NSMutableDictionary *cityDic in [provinceDic objectForKey:@"cityArray"]) {
NSLog(@"城市:%@", [cityDic objectForKey:@"cityName"]);
for (NSString *areaName in [cityDic objectForKey:@"areaArray"]) {
NSLog(@"地区:%@", areaName);
}
}
}
知识点:
1.属性的内存管理
语义特性 使用范围
assign 基本数据类(char, short, int, float, double)
retain 对象类型
copy 对象类型,且遵守了<NSCopying>协议
2.集合的内存管理
1️⃣常见的集合类有:NSArray,NSDictionary,NSSet。
2️⃣集合会自主管理集合内部元素。
3️⃣集合内存管理的方式:
加入集合的元素会被retain;
移除出集合的元素会被release;
集合被释放时,会对集合中所有元素release;
3.KVC
1️⃣KVC:Key Value Coding,键值编码,是一种间接访问实例变量的方法。
2️⃣KVC提供了一种使用字符串(key)而不是访问器方法,去访问一个对象实例变量的机制。
3️⃣KVC中常用的方法:
- (id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;
4️⃣KVC按顺序使用如下技术:
1)、检查是否存在getter方法-<key>或者setter方法-set<key>:的方法;
2)、如果没有上述方法,则检查是否存在名字为-_<key>、<key>的实例变量;
3)、如果仍未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。
4.ARC
ARC:Automatic Reference Counting,自动引用计数,由开发人员开辟内存空间,但是不需要释放该内存空间,由系统自动释放该空间。
ARC本质上还是基于MRC的,只不过是系统自动添加了释放内存的方法。
ARC是编译器特性,而不是运行时特性,更不是垃圾回收器(GC)。
从Xcode5.0后,创建的工程默认是开启ARC的。
当工程开启ARC后,由于编译器会自动帮你释放内存,所有和内存相关操作retain、release、autorelease,都不能写。
当重写dealloc方法时, 也不能写[super dealloc],否则会报错。
ARC中的属性语义特性:
语义特性 使用范围
assign 基本数据类(char, short, int, float, double)
strong 对象类型,相当于MRC中的retain
copy 对象类型,且遵守了<NSCopying>协议
weak 对象类型,但是内部不会对对象做retain的操作
ARC与MRC的混编:
如果需要对特定文件开启或关闭ARC,可以在工程选项中选择Targets -> Compile Phases -> Compile Sources,在里面找到对应文件,添加flag:
打开ARC:-fobjc-arc
关闭ARC:-fno-objc-arc
5.总结
1️⃣assign、retain、copy对应不同的setter实现。为实例变量赋值时,尽量使用setter方法,再次赋值时,会把之前值release。
2️⃣dealloc在对象引用计数为0时自动调用,不要显示调用。dealloc实现内部,先要释放实例变量,然后执行[super dealloc]。
3️⃣便利构造器的内存管理是借助autorelease实现的。
4️⃣集合会管理自己的元素。
5️⃣KVC是一种间接访问实例变量的方法。
6️⃣ARC系统管理内存,不需要开发人员手动管理。