iOS 之 Foundation 框架

来源:互联网 发布:java 弱引用 gc 编辑:程序博客网 时间:2024/04/27 14:31

我们前面的章节中就一直新建Cocoa Class,那么Cocoa到底是什么,它和我们前面以及后面要讲的内容到底有什么关系呢?Objective-C开发中经常用到NSObject,那么这个对象到底是谁?它为什么又出现在Objective-C中间呢?今天我们将揭开这层面纱,重点分析在IOS开发中一个重要的框架Foundation,今天的主要内容有:

Foundation概述
常用结构体
日期
字符串
数组
字典
装箱和拆箱
反射
拷贝
文件操作
归档
Foundation概述
为什么前面说的内容中新建一个类的时候我们都是选择Cocoa Class呢?Cocoa是什么呢?

Cocoa不是一种编程语言(它可以运行多种编程语言),它也不是一个开发工具(通过命令行我们仍然可以开发Cocoa程序),它是创建Mac OS X和IOS程序的原生面向对象API,为这两者应用提供了编程环境。

我们通常称为“Cocoa框架”,事实上Cocoa本身是一个框架的集合,它包含了众多子框架,其中最重要的要数“Foundation”和“UIKit”。前者是框架的基础,和界面无关,其中包含了大量常用的API;后者是基础的UI类库,以后我们在IOS开发中会经常用到。这两个框架在系统中的位置如下图:

Cocoa

其实所有的Mac OS X和IOS程序都是由大量的对象构成,而这些对象的根对象都是NSObject,NSObject就处在Foundation框架之中,具体的类结构如下:

Foundation1

Foundation2

Foundation3

通常我们会将他们分为几类:

值对象
集合
操作系统服务:文件系统、URL、进程通讯
通知
归档和序列化
表达式和条件判断
Objective-C语言服务
UIKit主要用于界面构架,这里我们不妨也看一下它的类结构:

UIKit

常用结构体
在Foundation中定义了很多常用结构体类型来简化我们的日常开发,这些结构体完全采用Objective-C定义,和我们自己定义的结构体没有任何区别,之所以由框架为我们提供完全是为了简化我们的开发。常用的结构体有NSRange、NSPoint、NSSize、NSRect等

//
// main.m
// FoundationFramework
//
// Created by Kenshin Cui on 14-2-16.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

import

import

import

import

import

import

import “Person.h”

void test1(){
//NSArray长度不可变所以初始化的时候就赋值,并且最后以nil结尾
//此外需要注意NSArray不能存放C语言的基础类型
NSObject *obj=[[NSObject alloc]init];
//NSArray *array1=[[NSArray alloc] initWithObjects:@”abc”,obj,@”cde”,@”opq”, nil];
NSArray *array1=[NSArray arrayWithObjects:@”abc”,obj,@”cde”,@”opq”,@25, nil];
NSLog(@”%zi”,array1.count);//数组长度,结果:5
NSLog(@”%i”,[array1 containsObject:@”cde”]);//是否包含某个对象,结果:1
NSLog(@”%@”,[array1 lastObject]);//最后一个对象,结果:25
NSLog(@”%zi”,[array1 indexOfObject:@”abc”]);//对象所在的位置:0

Person *person1=[Person personWithName:@"Kenshin"];Person *person2=[Person personWithName:@"Kaoru"];Person *person3=[Person personWithName:@"Rosa"];NSArray *array2=[[NSArray alloc]initWithObjects:person1,person2,person3, nil];[array2 makeObjectsPerformSelector:@selector(showMessage:) withObject:@"Hello,world!"];//执行所有元素的showMessage方法,后面的参数最多只能有一个/*结果: My name is Kenshin,the infomation is "Hello,world!". My name is Kaoru,the infomation is "Hello,world!". My name is Rosa,the infomation is "Hello,world!". */

}
//数组的遍历
void test2(){
NSObject *obj=[[NSObject alloc]init];
NSArray *array=[[NSArray alloc] initWithObjects:@”abc”,obj,@”cde”,@”opq”,@25, nil];
//方法1
for(int i=0,len=array.count;i

import

import “Person.h”

void test1(){
Person *person1=[Person personWithName:@”Kenshin”];
Person *person2=[Person personWithName:@”Kaoru”];
Person *person3=[Person personWithName:@”Rosa”];
NSMutableArray *array1=[NSMutableArray arrayWithObjects:person1,person2,person3, nil];
NSLog(@”%@”,array1);
/*结果:
(
“name=Kenshin”,
“name=Kaoru”,
“name=Rosa”
)
*/

Person *person4=[Person personWithName:@"Jack"];//此时person4的retainCount为1[array1 addObject:person4];//添加一个元素,此时person4的retainCount为2NSLog(@"%@",array1);/*结果: (     "name=Kenshin",     "name=Kaoru",     "name=Rosa",     "name=Jack" ) */[array1 removeObject:person3];//删除一个元素NSLog(@"%@",array1);/*结果: (     "name=Kenshin",     "name=Kaoru",     "name=Jack" ) */[array1 removeLastObject];//删除最后一个元素,//此时person4的retainCount为1NSLog(@"%@",array1);/*结果: (     "name=Kenshin",     "name=Kaoru" ) */[array1 removeAllObjects];//删除所以元素//注意当往数组中添加一个元素时会retain因此计数器+1,当从数组中移除一个元素时会release因此计数器-1//当NSMutalbeArray对象release的时候会依次调用每一个对象的release

}
void test2(){
NSMutableArray *array1=[NSMutableArray arrayWithObjects:@”1”,@”3”,@”2”, nil];
NSLog(@”%@”,array1);
/*结果:
(
1,
3,
2
)
*/

NSArray *array2= [array1 sortedArrayUsingSelector:@selector(compare:)];//注意这个方法没有修改array1NSLog(@"%@",array1);/*结果: (     1,     3,     2 ) */NSLog(@"%@",array2);/*结果: (     1,     2,     3 ) */[array1 sortUsingSelector:@selector(compare:)];//这个方法会修改array1NSLog(@"%@",array1);/*结果: (     1,     2,     3 ) */

}

int main(int argc, const char * argv[]) {

test1();test2();return 0;

}
可变数组中的元素后面必须加nil以表示数据结束;
往一个可变数组中添加一个对象,此时这个对象的引用计数器会加1,当这个对象从可变数组中移除其引用计数器减1。同时当整个数组销毁之后会依次调用每个对象的releaes方法。
在不可变数组中无论对数组怎么排序,原来的数组顺序都不会改变,但是在可变数组中如果使用sortUsingSelector:排序原来的数组顺序就发生了变化。

字典
字典在我们日常开发中也是比较常用的,通过下面的代码我们看一下在ObjC中的字典的常用操作:初始化、遍历、排序

//
// main.m
// FoundationFramework
//
// Created by Kenshin Cui on 14-2-16.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

import

import

import

import

import

import

import

import “Account.h”

@implementation Account

@end
Person.h

//
// Person.h
// FoundationFramework
//
// Created by Kenshin Cui on 14-2-16.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

import

import “Person.h”

@implementation Person

-(Person )initWithName:(NSString )name{
if(self=[super init]){
self.name=name;
}
return self;
}

+(Person )personWithName:(NSString )name{
Person *person=[[Person alloc]initWithName:name];
return person;
}

-(void)showMessage:(NSString *)infomation{
NSLog(@”My name is %@,the infomation is \”%@\”.”,_name,infomation);
}

//自己实现对象比较方法
-(NSComparisonResult)comparePerson:(Person *)person{
return [_name compare:person.name];
}

-(NSString *)description{
return [NSString stringWithFormat:@”name=%@”,_name];
}

@end
main.m

//
// main.m
// FoundationFramework
//
// Created by Kenshin Cui on 14-2-16.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

import

import “Person.h”

int main(int argc, const char * argv[]) {
/常用方法/
Person *person1=[Person personWithName:@”Kenshin”];
NSLog(@”%i”,[person1 isKindOfClass:[NSObject class]]); //判断一个对象是否为某种类型(如果是父类也返回YES),结果:1
NSLog(@”%i”,[person1 isMemberOfClass:[NSObject class]]); //判断一个对象是否是某个类的实例化对象,结果:0
NSLog(@”%i”,[person1 isMemberOfClass:[Person class]]); //结果:1
NSLog(@”%i”,[person1 conformsToProtocol:@protocol(NSCopying)]);//是否实现了某个协议,结果:0
NSLog(@”%i”,[person1 respondsToSelector:@selector(showMessage:)]);//是否存在某个方法,结果:1

[person1 showMessage:@"Hello,world!"];//直接调用一个方法[person1 performSelector:@selector(showMessage:) withObject:@"Hello,world!"];//动态调用一个方法,注意如果有参数那么参数类型只能为ObjC对象,并且最多只能有两个参数/*反射*///动态生成一个类NSString *className=@"Person";Class myClass=NSClassFromString(className);//根据类名生成类Person *person2=[[myClass alloc]init]; //实例化person2.name=@"Kaoru";NSLog(@"%@",person2);//结果:name=Kaoru//类转化为字符串NSLog(@"%@,%@",NSStringFromClass(myClass),NSStringFromClass([Person class])); //结果:Person,Person//调用方法NSString *methodName=@"showMessage:";SEL mySelector=NSSelectorFromString(methodName);Person *person3=[[myClass alloc]init];person3.name=@"Rosa";[person3 performSelector:mySelector withObject:@"Hello,world!"]; //结果:My name is Rosa,the infomation is "Hello,world!".//方法转化为字符串NSLog(@"%@",NSStringFromSelector(mySelector)); //结果:showMessage:return 0;

}
拷贝
对象拷贝操作也比较常见,在ObjC中有两种方式的拷贝:copy和mutablecopy,这两种方式都将产生一个新的对象,只是后者产生的是一个可变对象。在ObjC中如果要想实现copy或者mutablecopy操作需要实现NSCopy或者NSMutableCopy协议,拷贝操作产生的新的对象默认引用计数器是1,在非ARC模式下我们应该对这个对象进行内存管理。在熟悉这两种操作之前我们首先需要弄清两个概念:深复制(或深拷贝)和浅复制(或浅拷贝)。

浅复制:在执行复制操作时,对于对象中每一层(对象中包含的对象,例如说属性是某个对象类型)复制都是指针复制(如果从引用计数器角度出发,那么每层对象的引用计数器都会加1)。
深复制:在执行复制操作时,至少有一个对象的复制是对象内容复制(如果从引用计数器角度出发,那么除了对象内容复制的那个对象的引用计数器不变,其他指针复制的对象其引用计数器都会加1)。
注:

指针拷贝:拷贝的是指针本身(也就是具体对象的地址)而不是指向的对象内容本身。

对象复制:对象复制指的是复制内容是对象本身而不是对象的地址。

完全复制:上面说了深复制和浅复制,既然深复制是至少一个对象复制是对象内容复制,那么如果所有复制都是对象内容复制那么这个复制就叫完全复制。

对比copy和mutablecopy其实前面我们一直还用到一个操作是retain,它们之间的关系如下:

retain:始终采取浅复制,引用计数器会加1,返回的对象和被复制对象是同一个对象1(也就是说这个对象的引用多了一个,或者说是指向这个对象的指针多了一个);

copy:对于不可变对象copy采用的是浅复制,引用计数器加1(其实这是编译器进行了优化,既然原来的对象不可变,复制之后的对象也不可变那么就没有必要再重新创建一个对象了);对于可变对象copy采用的是深复制,引用计数器不变(原来的对象是可变,现在要产生一个不可变的当然得重新产生一个对象);

mutablecopy:无论是可变对象还是不可变对象采取的都是深复制,引用计数器不变(如果从一个不可变对象产生一个可变对象自然不用说两个对象绝对不一样肯定是深复制;如果从一个可变对象产生出另一个可变对象,那么当其中一个对象改变自然不希望另一个对象改变,当然也是深复制)。

注:

可变对象:当值发生了改变,那么地址也随之发生改变;

不可变对象:当值发生了改变,内容首地址不发生变化;

引用计数器:用于计算一个对象有几个指针在引用(有几个指针变量指向同一个内存地址);

//
// main.m
// FoundationFramework
//
// Created by Kenshin Cui on 14-2-16.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

import

import

import “Person.h”

@implementation Person

-(NSString *)description{
return [NSString stringWithFormat:@”name=%@,age=%i”,_name,_age];
}

//实现copy方法
-(id)copyWithZone:(NSZone *)zone{
//注意zone是系统已经分配好的用于存储当前对象的内存
//注意下面创建对象最好不要用[[Person allocWithZone:zone]init],因为子类如果没有实现该方法copy时会调用父类的copy方法,此时需要使用子类对象初始化如果此时用self就可以表示子类对象,还有就是如果子类调用了父类的这个方法进行重写copy也需要调用子类对象而不是父类Person
Person *person1=[[[self class] allocWithZone:zone]init];
person1.name=_name;
person1.age=_age;
return person1;
}

@end
main.m

//
// main.m
// FoundationFramework
//
// Created by Kenshin Cui on 14-2-16.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

import

import “Account.h”

import “Person.h”

void test1(){
Person *person1=[[Person alloc]init];
NSMutableString *str1=[NSMutableString stringWithString:@”Kenshin”];
person1.name=str1;
//由于name定义的时候使用属性参数采用的是copy策略,而根据前面的知识我们知道NSMutableString的copy策略采用的是对象内容复制,因此如果修改str1不会改变person1.name
[str1 appendString:@” Cui”];
NSLog(@”%@”,str1);//结果:Kenshin Cui
NSLog(@”%@”,person1.name); //结果:Kenshin

}

void test2(){
Person *person1=[[Person alloc]init];
person1.name=[NSMutableString stringWithString:@”Kenshin”];
person1.age=28;
Person *person2=[person1 copy];
NSLog(@”%@”,person1); //结果:name=Kenshin,age=0
NSLog(@”%@”,person2); //结果:name=Kenshin,age=0

[person2.name appendString:@" Cui"];NSLog(@"%@",person1);//结果:name=Kenshin Cui,age=28NSLog(@"%@",person2);//结果:name=Kenshin Cui,age=28

}

int main(int argc,char *argv[]){
test1();
test2();
return 0;
}
在上面的代码中重点说一下test2这个方法,在test2方法中我们发现当修改了person2.name属性之后person1.name也改变了,这是为什么呢?我们可以看到在Person.m中自定义实现了copy方法,同时实现了一个浅拷贝。之所以说是浅拷贝主要是因为我们的name属性参数是直接赋值完成的,同时由于name属性定义时采用的是assign参数(默认为assign),所以当通过copy创建了person2之后其实person2对象的name属性和person1指向同一个NSMutableString对象。通过图形表示如下:

MemoryStore2

上面test2的写法纯属为了让大家了解复制的原理和本质,实际开发中我们很少会遇到这种情况,首先我们一般定义name的话可能用的是NSString类型,根本也不能修改;其次我们定义字符串类型的话一般使用(copy)参数,同样可以避免这个问题(因为NSMutableString的copy是深复制)。那么如果我们非要使用NSMutabeString同时不使用属性的copy参数如何解决这个问题呢?答案就是使用深复制,将-(id)copyWithZone:(NSZone *)zone方法中person1.name=_name改为,person1.name=[_name copy];或person1.name=[_name mutablecopy]即可,这样做也正好满足我们上面对于深复制的定义。

补充-NSString的引用计数器

在好多语言中字符串都是一个特殊的对象,在ObjC中也不例外。NSString作为一个对象类型存储在堆中,多数情况下它跟一般的对象类型没有区别,但是这里我们需求强调一点那就是字符串的引用计数器。

//
// main.m
// FoundationFramework
//
// Created by Kenshin Cui on 14-2-16.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

import

import

import

import

import

import “Person.h”

@implementation Person

pragma mark 解码

-(id)initWithCoder:(NSCoder *)aDecoder{
NSLog(@”decode…”);
if (self=[super init]) {
self.name=[aDecoder decodeObjectForKey:@”name”];
self.age=[aDecoder decodeInt64ForKey:@”age”];
self.height=[aDecoder decodeFloatForKey:@”heiht”];
self.birthday=[aDecoder decodeObjectForKey:@”birthday”];
}
return self;
}

pragma mark 编码

-(void)encodeWithCoder:(NSCoder *)aCoder{
NSLog(@”encode…”);
[aCoder encodeObject:_name forKey:@”name”];
[aCoder encodeInt64:_age forKey:@”age” ];
[aCoder encodeFloat:_height forKey:@”height”];
[aCoder encodeObject:_birthday forKey:@”birthday”];

}

pragma mark 重写描述

-(NSString *)description{
NSDateFormatter *formater1=[[NSDateFormatter alloc]init];
formater1.dateFormat=@”yyyy-MM-dd”;
return [NSString stringWithFormat:@”name=%@,age=%i,height=%.2f,birthday=%@”,_name,_age,_height,[formater1 stringFromDate:_birthday]];
}

@end
main.m

//
// main.m
// FoundationFramework
//
// Created by Kenshin Cui on 14-2-16.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

import

import “Person.h”

int main(int argc,char *argv[]){

//归档Person *person1=[[Person alloc]init];person1.name=@"Kenshin";person1.age=28;person1.height=1.72;NSDateFormatter *formater1=[[NSDateFormatter alloc]init];formater1.dateFormat=@"yyyy-MM-dd";person1.birthday=[formater1 dateFromString:@"1986-08-08"];NSString *path1=@"/Users/kenshincui/Desktop/person1.arc";[NSKeyedArchiver archiveRootObject:person1 toFile:path1];//解档Person *person2= [NSKeyedUnarchiver unarchiveObjectWithFile:path1];NSLog(@"%@",person2);/*结果: name=Kenshin,age=28,height=0.00,birthday=1986-08-08 */return 0;

}
今天的文章就到这里了,内容确实不少,但是要用一篇文章把Foundation的所有内容说完也是完全不现实的。这篇文章有一部分内容并没有详细的解释代码,这部分内容主要是个人认为比较简单,通过注释大家就可以理解。不过并不是说这部分内容不重要,而是这些内容多数是记忆性的东西,不需要过多解释。

0 0
原创粉丝点击