读《Objective-C基础教程》学习笔记

NSLog比printf添加了特性,比如时间戳、日期戳、自动附加换行符等
双引号种的字符串前有一个@符号,这表示引用的字符串应该作为Cocoa的NSString元素来处理
BOOL实际上是一种对带符号的字符类型(signed char)的定义(typedef),它用8位存储空间
NSLog输出任意对象的值时,都会使用%@格式说明,在使用这个说明符时,对象通过一个名为description的方法提供自己的NSLog格式
int main(int argc, const char *argv[]),argc保存启动参数的数目,因为程序名常用作启动参数传递,所以argc值常为1或更大。argv[0]保存程序名
id是一种泛型,用于表示任何种类的对象。
Objective-C有一种名为中缀符(infix notation)的
语法技术。方法的名称及其参数都是合在一起的。
当代码发送消息时,Objectivew-C的方法调度程序将在当前类种搜索相应的方法。如果调度程序无法在接收消息的对象类中找到相应的方法,它就在该对象的超类中进行查找。
- (NSString *)description可重写用于对对象的描述
将[super init]的结果赋给self是Objective-C的标准惯例。这么做是为了防止超类在初始化过程中返回的对象不同于原先创建的对象。
%@只是调用每个对象的description方法并显示结果
如果用.mm做文件扩展名,编译器就会认为你是用Objective-C++编写代码,这样你就可以同时使用C++语言和Objective-C来编程了
修改每个文件中注释部分的公司名称:在Terminal上输入defaults write com.apple.Xcode PBXCustomTemplateMacroDefinitions '{"ORGANIZATIONNAME" = "名字"}'
- (NSComparisonResult) compare:(NSString *)string options:(unsigned)mask;可以实现不区分大小写的比较
rangeOfString:检查字符串内的某处是否包含其他字符串,如果传递的参数在接收的字符串中没有找到,那么返回的NSRange变量中range.start则等于NSNotFound
任何使用NSString的地方,都可以使用NSMutableString来代替,任何接受NSString的方法也都会接受NSMutableString
NSArray只能存储Objective-C的对象,而不能存储C语言中的基本数据类型,如int、float、enum、struct,或者NSArray中的随机指针。同时也不能再NSArray中存储nil(对象的零值或NULL值)
Core Foundation框架与Cocoa一样,但它是用C语言实现的,它的大部分代码都是开源的,Core Foundation框架中的许多对象和Cocoa对象之间是免费桥接的,就是说它们是可以互换使用的
容量数值之所以存在,是为了Cocoa能够对代码进行一些优化。Cocoa既不会将对象预写入数组,也不会用该容量值来限制数组的大小
NSEnumerator,是Cocoa用来描述这种集合迭代运算的方式。要想使用NSEnumerator,需通过objectEnumerator向数组请求枚举器。如果想从后向前浏览集合,还有一个方法reverseObjectEnumerator可以使用。
例子:
NSEnumerator *enumerator;
enumerator = [array objectEnumerator];
id thingie;
while(thing = [enumerator nextObject])
{
 NSLog(@"I found <a href="mailto:%@" ,thing"="" style="text-decoration: none; color: rgb(9, 67, 130); ">%@",thing);
}
对可变数组进行枚举操作时,有一点需要注意:不能通过添加或删除对象这类方式改变数组容器。
快速枚举实例:
for(NSString *string in array)
{
 NSLog(@"I found %@", string);
}
在Cocoa中,许多类实际上是以类簇的方式实现的,即它们是一群隐藏在通用接口之下的与实现相关的类。给一个类簇创建子类是一件非常痛苦和令人沮丧的事情。
NSNumber类来包装(以对象形式实现)基本数据类型,创建NSNumber之后,可以把它放入一个字典或者数组中
NSNumber实际上是NSValue的子类,NSValue可以包装任意值。可以用NSValue将结构放入NSArray和NSDictionary中。@encode编译器指令可以接受数据类型的名称并生成合适的字符串
实例:
NSRect rect = NSMakeRect(1,2,30,40);
NSValue *value;
value = [NSValue valueWithBytes:&rect objCType:@encode(NSRect)];
NSNull大概是Cocoa里最简单的类了,只有一个方法:+(NSNull *)null;
NSDirectoryEnumerator是NSEnumerator的子类
[@"~" stringByExpandingTildeIndex];可以接受~字符并将其展开
当使用alloc、new方法或者通过copy消息(生成接收对象的一个副本)创建一个对象时,对象的保留计数器值被设置为1。要增加对象的保留计数器值,可以给对象发送一条retain消息。要减少对象的保留计数器值,可以给对象发送一条release消息。
当一个对象因其保留计数器归0而即将被销毁时,Objective-C自动向对象发送一条dealloc消息。获得保留计数器的当前值,可以发送retainCount消息。
当对象的保留计数器值归0时,将自动发送dealloc消息。
Cocoa中有一个自动释放池的概念——NSAutoreleasePool,是一个存放实体的池(集合),这些实体可能是对象,能够被自动释放。
NSObject类提供了一个autorelease方法:-(id)autorelease;该方法预先设定了一条在将来某个时间发送的release消息,其返回值是接收消息的对象。当给一个对象发送autorelease消息时,实际上是将该对象添加到NSAutoreleasePool中。当自动释放池被销毁时,会向该池中的所有对象发送release消息。
自动释放池的概念并不神秘,可以使用NSMutableArray来编写自己的自动释放池,以容纳对象并在dealloc方法中向池中的所有对象发送release消息。
创建一个自动释放池时,该池自动成为活动的池。释放该池时,其保留计数器值归0,然后该池被销毁。在销毁过程中,该池释放其包含的所有对象。
当使用AppKit时,Cocoa定期自动为你创建和销毁自动释放池。
-drain方法只是清空自动释放池而不销毁它。
Cocoa内存管理规则:
1.当你使用new、alloc或copy方法创建一个对象时,该对象的保留计数器值为1。当不再使用该对象时,你要负责像向该对象发送一条release或autorelease消息。这样,该对象将在其使用寿命结束时被销毁。
2.当你通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,不需要执行任何操作来确保该对象被清理。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它。
3.如果你保留了某个对象,你需要(最终)释放或自动释放该对象。必须保持retain方法和release方法的使用次数相等。
启动垃圾回收以后,通常的内存管理命令全都变成了空操作命令,不执行任何操作。
Objective-C的垃圾回收器是一种继承性的垃圾回收器。与那些已经存在了一段时间的对象相比,新创建的对象更可能被当成垃圾。垃圾回收器定期检查变量和对象以及它们之间的指针,当发现没有任何变量指向某个对象时,就将该对象视为应该被丢弃的垃圾。最糟糕的事情莫过于保留一个指向不再使用的对象的指针。因此,如果你在一个实例变量中指向某个对象,一定要在某个时候将该实例变量赋值为nil,以取消对该对象的引用并使垃圾回收器知道该对象可以被清理了。
当对象接收到一条autorelease消息时,其保留计数器值并不立即改变。相反,该对象只是被放入NSAutoreleasePool中。当自动释放池被销毁时,会向池中的所有对象发送release消息,所有被自动释放的对象都将其保留计数器值减1。如果保留计数器值归0,则对象被销毁。当使用AppKit时,Objective-C会在适当的时间为你创建和销毁自动释放池,例如在当前用户事件处理完毕时。
向某个类发送alloc消息的结果,就是为该类分配一块足够大的内存,以存放该类的全部实例变量。同时,alloc方法还将这块内存区域全部初始化为0。
指定初始化函数:类中的某个初始化方法被指派为指定初始化函数。该类的所有初始化方法使用指定初始化函数执行初始化操作。子类使用其超类的指定初始化函数实现超类的初始化。通常,接受参数最多的初始化方法最终成为指定初始化函数。
如果构造了一个初始化函数,则一定要在你自己的指定初始化函数中调用超类的指定初始化函数。
如果初始化函数不止一个,则要选择一个作为指定初始化函数。被选定的方法应该调用超类的指定初始化函数,要按照指定初始化函数的形式实现所有其他初始化函数。
@property是一种新的编译器功能,表示声明了一个新对象的属性,自动声明属性的setter和getter方法。
@synthesize也是一种新的编译器功能,表示“创建该属性的访问器”
如果点表达式出现在等号左边,该属性名称的setter方法将被调用。如果点表达式出现在对象变量右边,则该属性名称的getter方法将被调用。
nonatomic可以提高访问器方法的调用速度
如果不希望保留属性的对象,可以使用assign方法以避免保留周期问题。
如果在dealloc方法意外的地方清除特性,那么使用“将nil赋值给对象”的方法可以将特性设置为nil,同时可以使我们避免对已释放内存的悬空引用问题。(生成的访问器方法将自动释放以前的对象,并用nil代替对象)
类别使Objective-C允许扩展现有类(即使没有这些类的源代码)的一种技术。不能添加新实现变量。
声明方式:
@interface 类名(类别名)
方法。。。。
@end
类别的局限性:
1.无法向类中添加新的实例变量。类别没有位置容纳实例变量。
2.名称冲突,即类别中的方法与现有方法重名。当发生名称冲突时,类别具有更高的优先级。类别方法将完全取代初始方法,从而无法再使用初始方法。
类别的作用:将类的实现分散到多个不同文件或多个不同框架中,创建对私有方法的前向引用,以及向对象添加非正式协议。
Cocoa中的类经常使用一种名为委托(delegate)的技术,委托是一种对象,另一个类的对象会要求委托对象执行它的某种操作。
正式协议要求显示地采用协议。采用协议的办法是在类的@interface声明中列出协议的名称。采用协议意味着该类承诺实现该协议的所有方法。
浅层复制不复制引用对象,新复制的对象只指向现有的引用对象。深层复制将复制所有的引用对象。
id类型表示一个可以指向任何类型的对象的指针,它是一个泛型对象类型。
Objective-C 2.0增加了两个新的协议修饰符:@optional和@required。
可以通过在@protocol部分列出一组方法名来定义一个正式协议。通过在@interface声明中的类名之后列出用尖括号括起来的协议名称,对象可以采用协议。当对象采用一个正式协议时,它承诺实现该协议中列出的每一个要求实现的方法。如果你没有实现协议中的所有方法,编译器将向你提出警告,从而帮助你履行自己的承诺。
IBOutlet和IBAction实际上只是AppKit提供的#define。IBOutlet的定义没有任何作用,因此将不会对它进行编译。IBAction定义为void,这意味着声明的方法的返回类型将是void。
.nib文件是包含被冻结的对象的二进制文件,而.xib文件是XML格式的nib文件,在编译时,.xib文件将编译为.nib文件。
在Cocoa中,有一类名为属性列表的对象,常简写为plist。这些列表包含Cocoa知道如何操作的一组对象。具体来讲,Cocoa知道如何将它们保存到文件中并进行加载。属性列表类包括NSArray、NSDictionary、NSString、NSNumber、NSDate、NSData,以及它们的变体。
集合属性列表类(NSArray、NSDictionary)具有一个-writeToFile:atomically:方法,用于将属性列表写入文件。NSString和NSData也具有该方法,但只能写出字符串和数据块。
对象可以将它们的实例变量和其他数据编码为数据块,然后保存到磁盘中。以后将这些数据块读回到内存中,并且还能基于保存的数据创建新对象。这个过程称为编码和解码,或者称为序列化和反序列化。(NSCoder协议)
NSCoder是一个抽象类,NSKeyedArchiver和NSKeyedUnarchiver是它的两个子类。
ArchivedDataWithRootObject:类方法编码对象。首先,它在后台创建一个NSKeyedArchiver实例;然后,它将NSKeyedArchiver实例传递给对象的-encodeWithCoder方法。整个对象集合完成对键和值的编码后,具有键/值对的归档程序将所有对象扁平化为一个NSData类并将其返回。
键/值编码称为KVC,它是一种间接更改对象状态的方式,其实现方法是使用字符串描述要更改的对象状态部分。
键/值编码中的基本调用包括-valueForKey:和-setValue:forKey:。以字符串的形式向对象发送消息,这个字符串是属性的关键。
valueForKey:首先查找以键-key或-isKey命名的getter方法。如果不存在getter方法,它将在对象内部查找名为_key或key的实例变量。
-valueForKey:在Objective-C运行时中使用元数据打开对象并进入其中查找需要的信息。在C或C++语言中不能执行这种操作。通过使用KVC,可以获取不存在getter方法的对象值,无需通过对象指针直接访问实例变量。
当使用setValueForKey时,它将自动标量值(int、float和struct)放入NSNumber或NSValue中;当使用-setValueForKey时,它自动将标量值从这些对象中取出。仅KVC具有这种自动包装功能。常规方法调用和属性语法不具备该功能。
-setValue:forKey:工作方式和-valueForKey:相同。首先查找名称的setter方法,如果不存在setter方法,将在类中查找名为name或_name的实例变量,然后为它赋值。
除了通过键设置值外,键/值编码还支持指定键路径,像文件系统路径一样,你可以遵循一系列关系来指定该路径。可以指定以圆点分割的不同属性名称。这些键路径的深度是任意的,具体取决于对象图(对象图是一种表示相关对象集合的方式)的复杂度。不能在键路径中为数组添加索引。
键路径不仅能引用对象值,还可以引用一些运算符来进行一些运算,@count,@sum,@avg,@distinctUnionOfObects
KVC需要解析字符串来计算你需要的答案,因此速度比较慢。此外,编译器无法对它进行错误检查。
-setNilValueForKey方法可以解决nil无法表示的问题。
valueForUndefinedKey:方法的重写来处理未定义的键。还可以使用setValue:forUndefinedKey:方法。
Cocoa提供了一个名为NSPredicate的类,它用于指定过滤器的条件。可以创建NSPredicate对象,通过该对象准确地描述所需的条件,对每个对象通过谓词进行筛选,判断它们是否与条件相匹配。
使用NSPredicate类方法+predicateWithFormat来实际创建谓词。如果谓词字符串的文本块未被引用,则该谓词字符串被看做是键路径;如果引用了文本块,则认为它是文本字符串。
-evaluateWithObject:通知接收对象(谓词)根据指定的对象计算自身的值。
-filteredArrayUsingPredicate:是NSArray数组中的一种类别方法,它将循环过滤数组内容,根据谓词计算每个对象的值,并将值为YES的对象累计到将被返回的新数组中。
使用谓词确实很便捷,但它的运行速度不会比你自己编写全部代码快。
通过NSPredicate字符串,也可以使用%K指定键路径。
可以使用predicateWithSubstitutionVariables调用来构造新的专用谓词。
运算符:
>、>=、=>、<、<=、=<、!=、<>、括号表达式、AND、OR、NOT、&&、||、!。
谓词字符串中的运算符不区分大小写。
SELF可以引用用于谓词计算的对象。可以将谓词中所有的键路径表示成对应的SELF。
BEGINSWITH:检查某个字符串是否可以另一个字符串开头;ENDSWITH:检查某个字符串是否可以另一个字符串结尾:CONTAINS:检查某个字符串是否在另一个字符串内部。
LIKE运算符,问号表示与一个字符匹配,星号表示与任意个字符匹配,LIKE还接受[cd]修饰符,用于忽略大小写和发音符号。
如果你热衷于正则表达式,可以使用MATCHES运算符。赋给该运算符一个正则表达式,谓词将会计算出它的值。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值