2012.7.17
第一章 基础知识
Object-C语言不允许多继承,也就是同时继承多个父类。因此,每一个类只有一个最直接的抽象父类。
Master-Detail Application:这个工程模板将会给你创建一个split View Controller 左右切换分割的应用程序,splitView Controller 将会在我们第二个章节的内容中提到.
Page-Based Application:这个模板将会帮你创建一个类似翻页的基础应用程序,你将会通过我们的第二章节学到更多关于这个模板的一些知识。
Empty Application:这个模板是一个空的工程程序,只是创建了一些简单的工程文件。没有一些基础的设置,一些基础的设置你都可以按照你的要求去添加。
声明一个变量:如果这个变量名字是一个单词,那么所有的单词都必须小写,如果这个变量名称是有多个单词组成,那么第一个单词必须全部小写,后面单词的每一个开头字母需要大写,其他的小写。比如"firstcounter"可以声明它为"firstCounter" ,变量名一般最好不要有下划线,变量名称最好只包含英文字母和数字,不要什么特殊符号。
NSInteger能用来保存有符号的(正或者负)的整形变量.
NSUInteger 用来表示无符号的,要么是正,要么是负的整形变量。
Float 浮点型的数据,例如1.23
NString string 类型的数据,比如"MrsThomson"
NSArray 其实也就是一个数组,比如说你有十个文件对象,那么你可以把他们保存在这个里面.NSSet 你可以保存唯一的,没有重复的集合对象。
我们使用双等于号,其实就是表示一个判断的过程,判断两个值是否相等。这样编译器在编译之后能得到一个 YES或者 NO的判断结果值。
NSInteger integer1 = 123; NSInteger integer2 = 456;
if (integer1 == integer2){ NSLog(@"Integers are equal.");
} else {
NSLog(@"Integers are not equal.");
}
如果你比较的是一些对象,那么你最好使用isEqual:方法来进行判断。实例代码如下:
NSObject *object1 = [[NSObject alloc] init]; NSObject *object2 = object1; NSLog(@"Both objects are equal."); } else { |
NSLog(@"Objects are not equal."); } |
可以按以下几步在.m文件中创建目标类:
- (BOOL) sendEmailTo:(NSString *)paramTo withSubject:(NSString *)paramSubject andEmailMessage:(NSString *)paramEmailMessage{ /* Send the email and return an appropriate value */ if ([paramTo length] == 0 || [paramSubject length] == 0 || [paramEmailMessage length] == 0){ |
} |
这是一个返回一个布尔值(Bool)实例方法,这个方法名为 sendEmailTo:WithSubject: andEmailMesssage:。并且它有三个参数。然后我们可以用下面的代码来调用这个方法:
[self sendEmailTo:@"[email protected]" withSubject:@"My Subject" andEmailMessage:@"Please read my email."];
如前所述,每一个参数的第一个名字(除了第一个)是可选的。换句话说,我们可以使用不同的名称来构造 sendEmailTo:WithSubject:andEmailMessage:方法:
- (BOOL) sendEmailTo:(NSString *)paramTo :(NSString *)paramSubject
:(NSString *)paramEmailMessage{
/* Send the email and return an appropriate value */ if (paramTo length] == 0 ||
[paramSubject length] == 0 || [paramEmailMessage length] == 0){ NSLog(@"Empty parameter(s) is/are provided."); return NO;
}
return YES;
}
强烈不建议写没有额外名称参数的方法。这是一个很糟糕的编程习惯,它会使你混淆,并且和你一起工作的团队会忽视你记录好的代码。我们可以这样调用这个方法:
[self sendEmailTo:@"[email protected]" :@"My Subject"
:@"Please read my email."];
分配初始化对象
必须先分配和初始化一个对象才能使用它。可以使用 alloc这个实例方法来分配一个对象。该类方法会为 对象、对象的实例和方法分配内存空间。但是,被分配的内存并未定义。所以,每个对象必须初始化,就是给它赋上初值。
一个初始化方法必须是特定的初始化,通常是具有很多参数的初始化方法。例如:initWithFrame:这个方法是 UIView 类的对象的一个特定初始化方法。所以,在使用对象之前先分配和初始化它。
当执行一个新的对象时,不要重载 alloc这个方法。这个方法是在声明在 NSObject 里的。相反的,而是为特定对象处理所需参数重载 init 方法和创建自定义方法。
MyObject *someObject = [[MyObject alloc] init];
添加类的属性
需要利用XXX.AA的方式来直接访问类中的变量,而不是采用调用方法的方式来访问.
使用关键字@property给类定义属性。
任何一个通过点表示的都是一个属性。属性是方法的快捷方式。什么意思呢?先看一个范例:
NSObject *myObject = [[NSObject alloc] init];
myObject.accessibilityHint = @"Some string";
到底属性是什么呢?当定义一个属性时,会告诉编译器我们将会给这个属性写上一个setter和getter方法。如果在这个属性里设置一个值,运行时将执行setter方法。如果读取属性,则执行的getter 方法。我们不必手动编写这两个方法的属性。我们可以在.m文件中使用@synthesize关键字让编译器自动为属性生成setter和gtter方法:
@property(nonatomic, copy) NSString *accessibilityHint;
假如要定义一个只读属性,你所需要做的就是用@readonly关键字定义它,例如:
@property (nonatomic, strong, readonly) NSString *lastName;
注意:
对于一个只读属性,改变它的值只有一个办法,即在该类中定义属性成员的变量。
使用协议委托任务(不太明白 等求证)
协议是一个声明某些方法及属性并储存在实体文档。(通常延伸档名是.h)任何实践协议的对象,都必须实践协议提供的方法及属性(可在协议中指定是必须或可选)
NSString类是不能更改的,NSString 类一旦被创建,内容就不能被修改了。可变字符串NSMutableString 创建以后还可以被修改。我们接下来很快会看到两种类的例子。
Objective-C字符串应该用双引号括起来。双引号的前面应该加上@符号。例如,”Hello,world”这个字符串在 Objective-C中应该表示为:
@” Hello, world”
有很多种方法可以把一个字符串赋给NSString 和NSMutableString 类的实例。如下:
NSString *simpleString = @"This is a simple string";
NSString *anotherString = [NSStringstringWithString:@"This is another simple string"];
NSString *oneMorestring =[[NSStringalloc] initWithString:@"One more!"];
NSMutableString *mutableOne = [NSMutableStringstringWithString:@"Mutable String"]; NSMutableString *anotherMutableOne = [[NSMutableStringalloc] initWithString:@"A retained one"]; NSMutableString *thirdMutableOne = [NSMutableStringstringWithString:simpleString]; |
运行时你可能不时的需要得到字符串的长度来做出某些操作。通过调用NSString 类或者其他子类例如NSMutableString 的实例的length 方法你可以实现这个功能。
if ([userName length] == 0)
另一个你可能想知道的关于字符串的功能是如何把一个字符串转换成整型。例如,字符串转int ,float 或double 类型。你可以使用NSString(或者它的子类)的 integerValue, floatValue,和 doubleValue 方法来获得int ,float 或double 类型的值。
例如:NSString *simpleString = @"123.456";
NSIntegerintegerOfString = [simpleString integerValue];
NSLog(@"integerOfString = %ld", (long)integerOfString);
CGFloatfloatOfString = [simpleString floatValue]; NSLog(@"floatOfString = %f", floatOfString); doubledoubleOfString = [simpleString doubleValue]; NSLog(@"doubleOfString = %f", doubleOfString);
integerOfString = 123 floatOfString = 123.456001 doubleOfString = 123.456000
如果你喜欢使用C 语言库里的字符串,就不需要使用@符号了。比如:char *cString = "This is a C String";
如果你想把一个NSString 转换成C 库里的字符串,必须用NSString 的UTF8String。例如:
const char *cString = [@"Objective-C String" UTF8String];
NSLog(@"cString = %s", cString);
你可以使用%S将一个 C字符串格式化输出到控制台。相应的,使用%@来格式输出NSString 对象。
要把一个C 字符串转换成NSString,必须使用 NSString 类的 stringWithUTF8String:方法,如下:
NSString *objectString = [NSString stringWithUTF8String:"C String"]; NSLog(@"objectString = %@", objectString); |
typedefstruct _NSRange { NSUInteger location; NSUInteger length; |
} NSRange;
如果你要查找的字符串(针)在一个目标字符串(草垛)中,NSRange结构的 location成员将被置为从零 开始的索引用来表示第一个针在草垛中的位置。如果草垛中不包含针,location值会给设为 NSNotFound。我们来看这个例子:
NSString *haystack = @"My Simple String"; NSString *needle = @"Simple";
NSRange range = [haystack rangeOfString:needle]; if (range.location == NSNotFound){
/* Could NOT find needle in haystack */
} else {
/* Found the needle in the haystack */ NSLog(@"Found %@ in %@ at location %lu", needle,
haystack,
(unsigned long)range.location); }
Tips:用来查询的 NSString类的 rangeOfString 方法是大小写敏感的。
在对象中使用整型或封装好的数字。
用NSNumber 类来用面向对象的方法处理数字。如果你只需要简单的数字(而不是对象),用NSInteger
类来操作有符号数(正数或者负数),用NSUInteger 类来操作无符号数(正数或0),用 CGFloat类和 double在操作浮点数。
正如我们用NSString 对象来存储字符串,我们可以用NSNumber 对象来存储数字。你可能会问为什么?
答案很简单:允许一个对象存储数字的值可以让我们方便的把数字存到磁盘,从磁盘取出。还能用一个简单的对象存储有符号、无符号以及浮点数,不要定义和转换各种不同类型的变量。变量的类型是无穷尽的。
让我们来看看NSNumber 对象的构造函数:
NSNumber *signedNumber = [NSNumbernumberWithInteger:-123456];
NSNumber *unsignedNumber = [NSNumber numberWithUnsignedInteger:123456]; NSNumber *floatNumber = [NSNumber numberWithFloat:123456.123456f]; NSNumber *doubleNumber = [NSNumber numberWithDouble:123456.1234567890];
用NSArray 和NSMutableArray 类把多个对象存储到操作相对更加方便的数组中。
你可以把任何NSObject 类或者它子类的对象放到NSArray 类型中。
NSArray和 NSMutableArray 的主要区别是NSMutableArray 可以在分配空间和初始化之后修改,然而NSArray 不可以被修改。
让我没来看一个例子。让我们创建一个NSString 实例,两个NSNumber 实例并且把它们存到一个不可变数组中:
NSString *stringObject = @"My String";
NSNumber *signedNumber = [NSNumbernumberWithInteger:-123]; NSNumber *unsignedNumber = [NSNumber numberWithUnsignedInteger:123]; NSArray *array = [[NSArrayalloc] initWithObjects:
stringObject,
signedNumber,
unsignedNumber, nil];
NSLog(@"array = %@", array);
你可以看到,我们用initWithObjects:来初始化 array。当你用这个初始化函数时,把你要存储的对象一个一个传递进去。最后用一个 nil符号结束这个列表以通知运行时列表什么时候结束。如果你不这么做,LLVM 编译器会抛出一个如下的异常:
warning: Semantic Issue: Missing sentinel in method dispatch
我们也可以用NSArray 类的arrayWithObjects:方法来创建一个可自动释放的数组。像这样:NSArray *array = [NSArray arrayWithObjects: stringObject,signedNumber,unsignedNumber, nil];
你可以调用数组的count 方法类得到数组中的对象个数。
你也可以通过 for循环或者枚举来遍历数组。让我们首先来看一下用 for循环解决的第一种方案。
NSArray *array = [NSArrayarrayWithObjects: stringObject, NSUInteger counter = 0; for (counter = 0; |
counter< [array count]; |
就像你看到的,我们用objectAtIndex:方法来获得一个指定位置的对象。
记住索引时从 0开始的。换句话说,当计数器到达-1时循环必须停止因为在数组中没有负索引。
可变数组非常有趣,就像你猜想的一样,不可变字符串一旦被分配了内存且初始化之后就不能修改了。但是数组在分配空间初始化之后可以被修改。让我们来看个例子:
NSString *stringObject = @"My String"; |
[arrayremoveObject:signedNumber]; [arrayaddObjectsFromArray:anotherArray]; for (id object in array){ |
分析这段代码之前,让我们来看下输出:
Object = My String Object = 123 Object = String 1 Object = String 2 Object = String 3
你可能会问发生了什么,那么让我们来看看我们都用了NSMutableArray 类的哪些方法:
addObject:
这个方法允许我们在个可变数组的末尾添加一个对象。
removeObject: 使用这个方法,我们可以删除数组中的一个指定对象。记住删除对象时,我们传递的参数是对象,而不是对象的索引。
想要通过索引来删除数组中的对象,必须使用removeObjectAtIndex 方法。
addObjectsFromArray:
通过这个方法,我们可以将一个可变或不可变数组添加到一个可变数组中。
请记住,在使用快速枚举或不可变数组的过程中,禁止向数组添加对象或许从数组删除对象,否则
就会引起运行时错误。这是可变数组快速排序时的默认行为。系统会自动实现。
数组排序
NSOrderedSame
参与比较的两个值相等。
NSOrderedAscending
左边的值比右边的小。这么来记:从左到右是升序的,意味着左边的值更小一些。
NSOrderedDescending
右边的值比左边的小。换句话说:从左到右是降序的,意味着左边的值更大一些。
使用NSDictionary 类以及可改变长度的NSMutableDictionary 类。
一个dictionary 是一个特殊数组,其中的每个项都包含一个key. 这事dictionary 和array 唯一不同的地方。
array的每一项(对象)都有一个数字的索引,然而dictionary 中的每一项(对象)都有一个key。解释一下,假设我们要把一个人的姓、名字以及年龄存储到一个 array和一个 dictionary中。我们应该按以下方式存储:
NSArray *person = [[NSArrayalloc] initWithObjects: @"Anthony", |
@"Robbins", |
可以看到我们用索引来获取array 中的对象。使用dictionaries 时,每个对象都有一个key 值。这个key 值是个对象,我们可以用它来访问dictionaries 中的值。
我们来看相同的例子用dictionaries 怎么做。我们有一个key"First Name"对应"Anthony"值,以此类推:
NSNumber *age = [NSNumber numberWithUnsignedInteger:51]; NSDictionary *person = [[NSDictionaryalloc] initWithObjectsAndKeys: @"Anthony", @"First Name", |
@"Robbins", @"Last Name", age, @"Age", |
NSLog(@"First Name = %@", [person objectForKey:@"First Name"]); NSLog(@"Last Name = %@", [person objectForKey:@"Last Name"]); NSLog(@"Age = %@", [person objectForKey:@"Age"]); |
First Name = Anthony Last Name = Robbins Age = 51 |
可以看到,我们用值和键的对儿初始化dictionary。我们在先传递值,然后传值对应的 key。当我们用 NSLog输出时,用key 作参数调用objectForKey:方法来输出 key对应的 value值。NSDictionary 对应的可改变dictionary 类NSMutableDictionary 在分配内存和初始化之后可以被修改。例如,如果在dictionary 初始化之后想从其中删除 Age这个 key对应的对象,我们应该这么做:
NSNumber *age = [NSNumber numberWithUnsignedInteger:51]; NSMutableDictionary *person = [[NSMutableDictionaryalloc] initWithObjectsAndKeys: @"Robbins", @"Last Name", age, @"Age", |
nil]; |
我们方便的删除了与Age 这个key 对应的对象。注意到"Age"不只为空了,而且以及不存在了。
如果想枚举dictionary 中所有的key,可以简单用 enumerateKeysAndObjectsUsingBlock:方法。在前文体到的 array 中,enumerateKeysAndObjectsUsingBlock方法会输出"FirstName" 和"Last Name",但没有"Age",因为已经被删除了。这个方法的参数是一个包含3 个参数而且没有返回值的块对象。3个参数如下:
Key
标识当前正在枚举的 key的一个 id。
Object
也是一个 id,标识当前 key对应的对象。
一个 BOOL型指针
在枚举的过程中,你可以随时通过给这个指针传一个YES 值来停止这个进程。如果想遍历dictionary 中所有key 就别动这个指针。
如果你不想用块对象来快速枚举,可以采用dictionary 的allKeys 方法来做。一旦得到所有的key 就可以通过 objectFor
Key:方法并用这些 key来找到对应的对象。像这样:
for (id keyInDictionary in [person allKeys]){
idobjectForKey = [person objectForKey:keyInDictionary];
NSLog(@"Key = %@, Object For Key = %@", keyInDictionary, objectForKey); }
用集合(sets)来代替数组。.
Sets和 array非常相似。二者最大的区别就是 sets中相同对象只能被添加一次。当你第二次添加同一个对
象时,sets会拒绝添加。
我们使用 NSSet类表示不可改变的 sets,用 NSMutableSet类表示可变 sets.我们来看一个不可变 sets的例子:
NSString *hisName = @"Robert";
NSString *hisLastName = @"Kiyosaki";
NSString *herName = @"Kim";
NSString *herLastName = @"Kiyosaki";
NSSet *setOfNames = [[NSSetalloc] initWithObjects: hisName,
hisLastName,
herName,
herLastName, nil];
NSLog(@"Set = %@", setOfNames);
你可以看到名字Kiyosaki 只添加了一次。Set拒绝将同一个对象加入列表两次。一个 set不仅仅比较对象 在内存中的位置,而且比较这些对象的内容,了解这点非常重要。hisLastName和 herLastName是两个不同的 变量,它们在内存中的地址不同。然而set 知道我们传递了两个NSString 的实例给它,而且可以比较这些字符串从而发现我们已经添加过了一个 Kiyosaki名字。所以 set中只留下了一个实例。
现在我们来看一下如何构造一个可变sets:
NSMutableSet *setOfNames = [[NSMutableSetalloc] initWithObjects: hisName,
hisLastName, nil];
[setOfNamesaddObject:herName]; [setOfNamesaddObject:herLastName];
我们可以方便的通过NSMutableSet 类的addObject:方法类像 set添加一个新的对象。你也可以用 removeObject方法来删除对象。同样要记住对象的内容,不只是地址。所以如果想从一个set 中删除一个字符串,可以将字符串传递给removeObject:方法,即使新字符串是内存中的不同变量。只要这个字符串/对象的内容相同你就可以得到想要的结果。
如果你想快速遍历一个set 中所有的对象,可以使用enumerateObjectsUsing Block:方法。传递给这个方法的块对象没有返回值,有两个参数:
一个 id类型的 key
包含 set中当前枚举的对象。
一个BOOL类型的指针
你可以随时将一个 boolean值 YES传给这个指针来停止遍历。 我们来看个例子。假设我们想在一个set 中查找字符串Kiyosaki:
[setOfNamesenumerateObjectsUsingBlock:^(__strong id obj, BOOL *stop) { if ([objisKindOfClass:[NSString class]]){
NSString *string = (NSString *)obj;
if ([string isEqualToString:@"Kiyosaki"]){
NSLog(@"Found %@ in the set", string); *stop = YES;
}
}
}];
如果能在在set 中找到一个Kiyosaki 值,我们将字符串打印到控制台。然后通过将YES 值传给遍历块对象的第二个参数来结束遍历。还有其他手动方法。用 count方法来获得 set中的对象个数。也可以用 allObjects方 法来获得set 中包含所有对象的数组。
如果想在set 中获取一个对象,调用set 的anyObject 方法。就像函数名代表的,这个函数返回set 中一个随机的对象。如果 set为空,则返回空。
注意不可变的空set 绝对是没有用的。它是空的而且在它的生命周期中一直为空。
程序包的一些知识(有待学习)
通过App 发布一条通知同时允许其他对象接收通知并采取行动,这取决于你发布的通知。
使用 NSNotificationCenter 中default notificationcenter 的postNotificationName:object:userInfo:的实例方法发
布一条通知,其中携带一个对象(通常此对象激活通知)和一条用户信息词典,词典中包含了关于词条通知和/或者激活通知的对象的额外信息。