第六章 源文件组织
文件组织
文件组织和C++类似,.h文件为头文件,用于保存@interface指令,公共struct定义,enum常量,#defines,extern全局变量等,一般来说文件名和类名相同;.m文件为每一个.h文件对应的实现内容,包含@implementation指令、全局变量的定义、私有struct等,文件名一般与.h的文件名相同
.mm文件扩展名,意味着这是Objective-C++,可以同时使用C++和Objective-C进行编程
Xcode创建文件
File -> New -> New File然后选择Objective Class
Xcode中有群组的概念,这会使得项目更加易于浏览
注意:在每一个.m文件的头部,你都需要添加一个#import语句,包含相应的.h文件
跨文件依赖关系
可能会出现由于两个文件互相依赖,那么如果文件本身发生了变化,那么就需要重新编译文件来适应这种变化,例如CarParts-Split.m依赖于Tire.h和Engine.h文件。如果两个文件中的任何一个发生变化,都需要重新编译CarPart-Split.m文件
@class关键词的意思是告诉编译器我只会通过指针来引用它,即创建了一个前向引用,现在编译器不知道这个是什么,但是以后会知道
例如以下代码(Car.h)
1 2 3 4 5 6 7 8 9 10 11 12 |
#import <Cocoa/Cocoa.h> @class Tire @class Engine @interface Car : NSObject - (void) setEngine: (Engine *) newEngine; - (Engine *) engine; - (void) setTire: (Tire *) tire atIndex:(int) index; - (Tire *) tireAtIndex: (int) index; - (void) print; @end // Car |
那么我们只需要在Car.m内部包含Tire.h和Engine.h即可
注意:如果出现类的继承,是不能使用@class的,当且仅当类中间只出现类的引用的时候才可以用@class
第七章 深入了解Xcode
1 2 3 4 5 6 7 8 9 10 11 |
@interface Mac: NSObject { BOOL hasXcode; } - (BOOL) hasXcode; @end // Mac @implementation Mac - (BOOL) hasXcode { return hasXcode; } |
第八章 Foundation Kit介绍
常用结构体
NSRange
1 2 3 4 |
typedef struct _NSRange{ unsigned int location; unsigned int length; }NSRange; |
其中location表示起始位置,length表示范围内所含元素的个数,参考C++的substr
创建一个NSRange有多种方式
- 直接赋值
- 结构体赋值方法{…}
- Cocoa中提供了一个快捷函数NSMakeRange(),这个函数会返回一个NSRange,通常用来在参数中传递,比较方便
CGPoint
1 2 3 4 |
typedef struct _CGPoint{ float x; float y; }CGPoint; |
表示二维笛卡尔平面的一个坐标
CGSize
1 2 3 4 |
typedef struct _CGSize{ float width; float height; }CGSize; |
用来存储长度和宽度
所以在之前的Shapes的程序中可以采用一个_CGPoint和一个CGSize来表示形状,但是Cocoa中存在这个类型
CGRect
1 2 3 4 |
typedef struct _CGRect{ CGPoint origin; CGSize size; }CGRect; |
对于以上三种数据类型,Cocoa中也有创建这些数据类型的快捷函数:CGPointMake()、CGSizeMake()、CGRectMake()
C语言中结构体的性能要大于OC中对象的时间,他会消耗大量的时间
类方法
在声明时使用加号声明,表示该方法是一个类方法类似于C语言的static关键字,即不需要创建实例都可以对该方法进行调用,与之相对应的是实例方法
注意:类方法不可以调用实例方法,只能调用类方法;类方法不可以使用成员变量,但是可以使用self;类方法可以被类和对象调用
NSString
stringWithFormat函数用于创建一个NSString,这个函数的参数和NSLog的值是一样的
如下所示:
1 |
NSString *height = [NSString stringWithFormat :@"Your height is %d feet, %d inches",5,11]; |
NSString的length方法可以处理Unicode字符,但是C语言的strlen不行
字符串比较函数isEqualToString,返回一个BOOL值表示两个字符串内容是否相同
注意:这里和C语言类似,不可以直接使用==运算符来比较两个字符串内容
compare函数,用于比较两个字符串字典序大小
compare: options: 方法给我们更多的选择空间,可用选项如下:
- NSCaseInsesitiveSearch大小写不敏感
- NSLiteralSearch区分大小写
- 比较字符串的字符个数,而不是字符串的值
我们如果要忽略大小写并按照字符个数进行排序,则需要进行如下操作:
1 2 3 |
if([thing1 compare: thing2 options: NSCaseInsensitiveSearch | NSNumericSearch == NSOrderedSame]{ NSLog (@"They match!"); } |
hasPrefix hasSuffix字面意思,是否拥有某个前缀或者后缀
rangeOfString字面意思,返回这个字符串在字符串位置的NSRange,没有找到的话range.location等价于NSNotFound
NSMutableString
NSString在创建出来过后是不可以修改的(可以理解为const string),但是NSMutableString可以,需要使用
1 |
+ (id) stringWithCapacity: (NSUInteger) capacity; |
来进行类型的转换,该函数会返回一个指定大小的NSMutableString,我们可以对这个类型的变量进行操作
1 2 3 |
- (void) appendString: (NSString *) aString; - (void) appendFormat: (NSString *) format, ...; - (void) deleteCharactersInRange: (NSRange) aRange; |
这三个函数是NSMutableString的一个实例方法,基本都是字面意思
NSArray
NSArray中可以存入任意类型的对象:NSString、Car、Shape、Tire或者其他想要存储的对象,甚至是其他数组或字典
注意:只能存储Objective-C的对象,不能存储原始C语言的基础数据类型,如int、float、enum、struct和NSArray中的随机指针,也不能存储对象的零值和NULL值
arrayWithObjects: 创建一个新的NSArray,并且以nil结束,例如:
1 |
NSArray *array = [NSArray arrayWithObjects:@”one”, @”two”, @”three”, nil];
1 |
也可以使用下面的方式来创建一个NSArray这个时候不必在结尾处补上nil
1 |
NSArray *array2 = @[@”one”, @”two”, @”three”];
1 |
方法- (NSUInteger) count;用于获取它所包含的对象个数
方法- (id)objectAtIndex: (NSUInteger) index;用于访问特定索引处的对象,和NSArray[1]类似
Tips:在索引大于数组中对象个数的时候,Cocoa在运行时会报NSRangeException错误
NSString中的- NSArray componentsSeparateByString: (NSString astring)方法用于将字符串切割成NSArray
NSArray中的- NSString componentsJoinedByString: (NSString astring)方法用于将NSArray中的元素合并成字符串,并用astring作为分隔符;
同样:NSArray也是不可以改变的,一般我们使用NSMutableArray类型,通过arrayWithCapacity创建一个可变数组
NSMutableArray
注意:不能通过字面量创建NSMutableArray
+ (id) arrayWithCapacity: (NSUInteger) numItems;用于可变数组的创建,长度为numItems;可以理解为C++vector的带一个unsigned参数的那个构造函数
– (void) addObject: (id) anObject;在数组末尾添加对象,相当于C++vector的push_back
– (void) removeObjectAtIndex: (NSUInteger) index;删除掉指定位置的元素,注意索引从0开始
迭代器
– (NSEnumerator *) objectEnumerator;
NSEnumerator就是迭代器
NSEnumerator的方法- (id) nextObject; 返回迭代器的下一个对象,如果不存在返回nil
快速枚举
类似这样的语法,和C++11中支持的遍历模式可以说完全相同
for(NSString *string in array)
代码块方法快速枚举
NSArray 方法- (void)enumerateObjectsUsingBlock: (void (^))(id obj, NSUInteger idx, BOOL *stop)) block;
可以并发执行,效率较高,用法如下:
1 2 3 |
[array enumerateObjectsUsingBlock: ^(NSString *string, NSUInteger index, BOOL *stop) { NSLog(@"I found %@", string) } |
NSDictionary
定义类似于C++中的map,不过多解释,和正常的一样,不可以随意添加和删除
可以使用字面量定义:@{key: value, …}
或者+ (id) dictionaryWithObjectsAndKeys: (id) firstObject, …;
该函数的参数以nil值作为结束
注意:这里面Objects在前,keys在后;字面量定义则完全相反
读取的时候可以使用NSDictionary[key]来读取value或者使用NSDictionary中的方法: – (id) objectForKey: (id) aKey;
NSMutableDictionary
可以随意添加和删除字典元素
方法+ (id) dictionaryWithCapacity: (NSUInteger) numItems;和上面差不多,创建一个大小为numItems的字典元素
方法- (void)setObject: (id)anObject forKey: (id)aKey; 字面意思,添加一个键值对,这个方法会用新的值去替换掉旧的值
方法- (void) removeObjectForKey: (id) aKey; 移除指定aKey对应的键值对
注意:同样没有适用于NSMutableDictionary的字面量初始化方法
NSNumber
由于NSDictionary中只支持对象而不支持存储基本类型数据,所以需要引入NSNumber — 一个适用于所有变量类型的一个类型
NSNumber一般用于封装基本数据类型
1 2 3 4 |
+ (NSNumber *) numberWithChar: (char) value; + (NSNumber *) numberWithInt: (int) value; + (NSNumber *) numberWithChar (char) value; + (NSNumber *) numberWithBool (BOOL) value; |
也可以用字面量创建对象:
1 2 3 4 5 6 7 8 |
NSNumber *number; number = @'X'; number = @12345; number = @12345ul; number = @12345ll; number = @123.45f; number = @123.45; number = @YES; |
此时这个就可以作为Object传入NSDictionary中了
可以使用下面的实例方法重新获取值
1 2 3 4 5 |
- (char) charValue; - (int) intValue; - (float) floatValue; - (BOOL) boolValue; - (NSString *) stringValue; |
NSValue
NSValue是NSNumber的子类,可以封装任意值
1 |
+ (NSValue *) valueWithBytes: (const void *) value objCType: (const char *)type; |
第一个参数是指针,第二个参数是数据类型,编译器指令@encode可以直接确认数据类型。
1 |
- (void) getValue: (void *)buffer; |
用于提取数值,参数是这个数值的变量地址
1 2 3 4 5 6 |
+ (NSValue) valueWithPoint: (NSPoint) aPoint; + (NSValue) valueWithSize: (NSSize) size; + (NSValue) valueWithRect: (NSRect) rect; - (NSPoint) pointValue; - (NSSize) sizeValue; - (NSRect)rectValue; |
NSNull
+ (NSNull *)null;
[NSNull null]表示这里面什么都没有,可以看作是一个Object,这个Object可以表示什么都没有这一件事情,但是nil的特殊性却保证了它不可以
查找文件实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
int main(int argc, const char * argv[]){ @autoreleasepool // 自动释放池 { NSFileManager *manager; manager = [NSFileManager defaultManager]; NSString *home; home = [@"~" stringByExpandingTildeInPath];// 将~扩展到全局路径 NSDirectoryEnumerator *direnum; direnum = [manager enumeratorAtPath: home];// 获取到该目录下文件系统的一个枚举器 NSMutableArray *files; files = [NSMutableArray arrayWithCapacity: 42]; NSString *filename; while(filename = [direnum nextObject]){ if ([[filename pathExtension] isEqualTo: @"jpg"]){ //查询文件名后缀是否与这个相同,isEqualTo表示两个对象比较,完全相同的话就是YES,注意没有. [file addObject: filename]; } } NSEnumerator *fileenum; fileenum = [files objectEnumerator]; while (filename = [fileenum nextObject]){ NSLog (@"%@", filename); } } return 0; }// main |
使用快速枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
int main(int argc, const char * argv[]){ @autoreleasepool { NSFileManager *manager; manager = [NSFileManager defaultManager]; NSString *home; home = [@"~" stringByExpandingTildeInPath];// 将~扩展到全局路径 NSDirectoryEnumerator *direnum; direnum = [manager enumeratorAtPath: home];// 获取到该目录下文件系统的一个枚举器 NSMutableArray *files; files = [NSMutableArray arrayWithCapacity: 42]; for(NSString *filename in [manager enumeratorAtPath: home]) { if([[filename pathExtension] isEqualTo: @"jpg"]) { [files addObject: filename] } } for(NSString *filename in files) { NSLog (@"%@", filename); } } return 0; }// main |
NSFileManager
该类允许了文件系统的交互,创建目录,删除文件,移动文件和获取文件信息等操作
第九章 内存管理
对象生命周期
对象生命周期包含诞生、生存、交友、最终死去
引用计数方法
每一个对象拥有一个与之相关联的整数,称为引用计数器。alloc、new方法或者通过copy消息创建对象时,计数器被设置为1,retain消息+1,release消息-1
即将被销毁时会发送一条dealloc消息,可以重写这个方法来释放掉已经分配的全部相关资源。可以理解为C++中的析构函数。
1 2 3 |
- (id) retain; - (oneway void) release; - (NSUInteger) retainCount; |
可以在接受其他消息的同时进行retain调用,例如[[car retain] setTire: tireAtIndex: 2]; 表示先吧car对象的计数器+1并执行setTire方法
retainCount方法,可以返回计数器的值
对象所有权
一个对象内有指向其他对象的实例变量,则称该对象拥有这些对象
我们在一个方法内部使用
1 2 3 4 5 6 |
- (void) setEngine: (Engine *)newEngine { [newEngine retain]; [engine release]; engine = newEngine; } |
以此来进行内存管理
自动释放池
给一个对象发送autorelease消息,就是将该对象添加到自动释放池中,当自动释放池被销毁时,会自动向该池中所有对象发送release消息
例如以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 |
- (NSString *) description { NSString *description; description = [[NSString alloc] initWithFormat: @"I am %d years old", 4]; return description; } - (NSString *) description { NSString *description; description = [[NSString alloc] initWithFormat: @"I am %d years old", 4]; return [description autorelease]; } |
第一种方法在结束的时候并不会销毁对象,第二种方法在返回过后会自动销毁对象
自动释放池的创建和销毁
通过@autoreleasepool关键词或者NSAutoreleasePool对象
前者用大括号括起来,后者在创建和释放之间的代码都会使用这个自动释放池
在OSX10.4以后,可以使用drain方法来清空自动释放池而不会销毁它
说一句没有关系的:使用alloc init进行对象的创建比new要来的好 原因
注意:arrayWithCapacity方法在返回的时候会自动将NSMutableArray设置为自动释放,所以我们将可变数组作为临时变量的时候不需要手动release
ARC
垃圾回收机制无法用在IOS应用程序上,所以苹果公司建议不要在自己的代码中使用autorelease方法
在苹果公司的解决方案中有ARC(自动引用计数功能)会在编译期间自动向你的代码中插入retain
release等
但是C型的指针数组等ARC是不会参与的,例如malloc创建的字符串数组
注意:
- ARC不支持属性名称以new开头的命名规则
- 属性不能只有一个read-only而没有内存管理特性,默认的特性是assign
弱引用
弱引用不会增加引用计数
归零弱引用:在指向的对象消失的时候,这些弱引用需要被设置成nil
在声明变量的时候使用__weak关键词或者对属性使用weak特性即@property(weak)
对应的强引用也有自己的__strong关键字和strong特性。
内存管理的关键字和特性是不能同时使用的
异常处理
需要开启Xcode中的Enable Objective-C Exceptions项
@try:定义测试的代码块
@catch():参数是NSException类型,用来处理已抛出的代码块
@finally:定义无论是否有抛出异常都会执行的代码块,这段代码总会执行
@throw:抛出异常
例如:
1 2 3 4 5 6 |
@try{ } @catch (MyCustomException *custom) { } @catch (NSException *exception) { } @catch (id value) { } @finally { } |
抛出异常:
1 2 3 |
NSException *theException = [NSException exceptionWithName: ...] @throw theException [theException raise] |
以上两种方式都可以抛出异常
异常的内存管理
处理异常的内存管理的话最好使用@try和@finally代码块,然后在finally里面处理内容
一般来讲,异常都是自动释放的,因为不知道什么时候会释放内存
但是异常内存的处理最好将Exception放在内存池之前,因为否则可能导致pool释放早于异常抛出,可能会出现问题
0 条评论