Objective-C 基于C 语言的,增加面向对象的相关特性。
NextStep是一个使用Objective-C编写的功能强大的工具包,里面包含大量的类库、结构体等,被APPLE收购后更名为Cocoa,但APPLE并未更改NextStep中的类库名,所以存在大量以NS为前缀的类名、结构体、枚举等;
Cocoa框架由Foundation Kit、App Kit两部分组成;
Foundation Kit:基础工具库; App Kit:UI库、高级对象等;GNUStep调试Objective-C:
gcc $(FileName) -o $(FileNameNoExt).exe -I D:\Libraries\Apple\GNUstep\System\Library\Headers -L D:\Libraries\Apple\GNUstep\System\Library\Libraries -lobjc -lgnustep-base -fconstant-string-class=NSConstantString 有多个需要编译的源文件时,各文件间用空格隔开; -I:头文件查找路径; -L:库文件查找路径; -l:需要连接的库文件; -fconstant-string-class=NSConstantString:常量字符串所使用的类;数据类型:
BOOL:不是对象类型,使用8位(一个字节)的整数进行表示,对于二进制表示大于8位的数据,取其低8位有效。只有YES和NO两个值(不是TRUE/FALSE); nil:空。nil可以回应消息,因此不必为空指针而烦恼; NSString:字符串。@"..."是字符串自面值的表示方式; class:本身以为指针,故该类型的变量前不需要*; id:泛型对象,可以表示任意类型的Objectiv-C对象。是一个结构体指针,其内部通过Class类型的isa指向了继承了NSObject的对象,故该类型变量前不需要*; SEL:类型选择器,用于表示Objective-C的一个方法,而且是一个已存在的方法,类似于C中的函数指针。通过@selector(MethodName)获取。由于本身已是指针,故不需要*; NSString:字符串,字面值用@"......"表示; NSString *StrName=[[NSString alloc] initWithString:@StrValue]:使用NSString字面值初始化NSString对象; NSString *StrName=[[NSString alloc] initWithCString:strCalue]:使用C字符串初始化NSString对象; +(NSString*) stringWithFormat:@"......",......:用格式化模版个时候字符串; -(BOOL) isEqualToString:(NSString*) StrName:比较两个字符串是否相等。==比较指针,对象比较用equal方法; -(NSComparisonResult) compare:(NSString*) StrName options:(NSStringCompareOptions) Options:详细比较两个字符串时候相等,如是否忽略大小写等。 返回值为enum类型,如NSOrderdSame表示相等。NSStringCompareOptions也是enum类型,常用的值有NSCaseInsensitiveSearch,NSNumericSearch,分别表示忽略大小写和字说相等。 由于NSStringCompareOptions的枚举值都是2的指数,所以可以用位运算操作,如:NSCaseInsensitiveSearch|NSNumericSearch,表示忽略大小并且字数相等; -(BOOL) hasPrefix:(NSString) ParamName:判断字符串是否以ParamName作为前缀。hasSufix判断后缀; -(NSRange) rangeOfString:(NSString*) ParamName:判断是否包含参数ParamName。返回值为结构体,如果包含,其location为包含字符串ParamName所在的起始位置,length为长度。 如果不包含,location为NSNotFound,length为0; componentsSeparatedByString:(NSString*) Separator:按照给定的字符串Separator将字符串分割为数组; length:返回字符串长度; NSMutableString:继承自NSString长度可变的字符串。NSMutableString一般使用类方法; +(NSMutableString*) stringWithCapacity:(int) Capacity:创建NSMutableString,Capacity指定预先分配的字符串长度; -(NSMutableString*) appendString:(NSString*) StrName:在原字符串后追加内容; -(void) deleteCharactersInRange:(NSRange) RangeName:删除指定范围内的字符串,常与rangeOfString方法联用; NSArray:数组,nil表示数组元素的结束。不能存储基本数据类型、enum、structur、nil,只能存储Objectiv-C的对象; count:返回数组元素个数; objectAtIndex:(int) idx:返回指定索引位置的数组元素; componentsJoinedByString:(NSString*) Joinor:按照给定的连接字符串Joinor将数组连接为字符串; NSMutableArray:长度可变的数组; +(NSMutableArray) arrayWithCapacity:(int) Capacity:创建长度为Capacity的数组; -(id) objectAtIndex:(int) Idx:返回NSMutableArray中指定索引位置中的元素; addObject:(id) ObjectName:在数组的末尾添加对象; removeObjectAtIndex:(int) Idx:移除数组中指定索引位置的元素; componentsJoinedByString:(NSString*) Joinor:同NSArray的componentsJoinedByString一样,按照指定的字符串拼将数组拼接为字符串; objectEnumerator:获取反转之后的数组迭代器,为NSEnumerator类型。NSEnumerator中的nextObject方法获取迭代器当前位置的下一个元素。使用迭代器时,不能对数组进行添加、删除操作; NSDictionary:字典(哈希表),用于存储key-value的数据结构。与Java中Map类似; +(NSDictionary) dictionaryWithObjectsAndKeys:(NSObject*) ValueN,(NSObject*) KeyN, ...... ,nil:以可变参数创建NSDictionary对象。可变参数中每两个参数组成一个value-key对,以nil表示结束; -(NSObject*) objectForKey:(NSObject*) Key:按照指定可key查找对应的alue; -(NSObject*) setObject:(NSObject*) Value forKey:(NSObject*) Key:向NSDictionary中添加key-value对; -(void) removeObjectForKey:(NSObject*) Key:移除NSDictionary对象中指定key的alue; -(NSEnumerator*) keyEnumerator:获取key的枚举器; NSSet:哈希Set,表示以hash方式计算存储为的集合,与Java中HashSet一致。NSSet中的每个对象都有一个唯一的hash值,重复的对象只能保留一个,因此引出对象比较的问题, 需要实现从NSObject继承而来的两个方法,即-(BOOL) isEqual:(id) ObjectName和-(NSUInteger) hash。与Java一样,两个相同的对象必须有相同的hashCode,所以这两个方法必须同时实现; +(NSSet*) setWithObjects:(id) Object,(id) ObjectN,......,nil:创建NSSet对象; -(NSEnumerator*) objectEnumerator:获取NSSet迭代器; NSMutableSet:长度可变的哈希Set; NSValue:封装类。对于以上几种容器,所操作的数据都是对象,对于基本数据类型,enum,struct,nil不适用,所以需要对其封装; +(NSValue*) valueWithBytes:(VarPointer) VarName,objCType:@encode(VarType):将类型为VarType的变量Varname封装为对象。 其中第一个参数为所要封装的数据的地址,第二个参数为描述数据累i系那个、大小的字符串,并用@encode指令包装数据所属的类型; -(void) getValue:(void*) Value:取出NSValue中中的数据内容。参数为一指针,getValue将传入的指针指向所存储的数据。一般Cocoa中getXX方法都是这个作用,所以类中的getter方法不以get作为前缀; NSNumber:NSValue的子类,用于封装基本数据类型,如int,char,float,BOOL等; +(NSNumber) numberWithChar:(char) ChrName:封装char类型基础数据; +(NSNumber) numberWithInt:(int) IntName:封装int类型基础数据; +(NSNumber) numberWithFloat:(float) FltName:封装float类型基础数据; +(NSNumber) num,berWithBool:(BOOL) BolName:封装BOOL类型基础数据; NSNull:将存储控制到集合类; +(NSNull) null:创建NSNull对象; NSDate:日期类型; +(NSDate) date:以当前日期创建NSDate对象; +(NSDate) dateWithTimeIntervalSinceNow:(int) TimeSpan:返回与当前时间相比,相差TimeSpan时刻的日期对象。TimeSpan可为负值; +(NSCalendarDate) dateWithString:(NSString*) StrDate calendarFormat:(NSString*) DateFamat:以指定的格式将字符串转化为NSClendarDate对象; 格式化字符串: %Y:四位数的年 %y:两位数的年 %B:月份的英文全些 %b:月份的英文简写 %m:两位数月份 %A:星期的英文全些 %a:星期的英文简写 %d:两位说日期 %H:24小时制的两位数小时 %l:12小时制的两位数小时 %p:显示A.M或P.M %M:两位数的分钟 %S:两位数的秒 %F:三位说的毫秒 %Z:显示时区的名字 %z:显示时区与标准时区的偏移时间 HHMM NSData:数据缓冲区,类似于Java中字节数组; +(NSData*) dataWithBytes:(const void*) Bytes length:(NSUinteger) length:第一个参数为所要缓冲数据的起始位置,第二个参数指定数据的长度; NSMutableData:长度可变的数据缓冲区; NSAutoreleasePool:未知类的声明:ClassName.h
Objective-C类的定义前必须先定义一个接口,该接口用于描述这个类的组成,包括成员变量,类变量,类方法,成员方法。接口的扩展名为.h,也就是C/C++中的头文件。
格式: #import Header static VarType VarName; @interface InterfaceName:SupperName{ AccessModeifers VarType VarName; ………… } -(ReturnType) MethodName:(ParamType) ParamName LabN:(ParamType) ParamName ………… ………… +(ReturnType) MethodName:(ParamType) ParamName LabN:(ParamType) ParamName ………… ………… @end 说明: 1.import:导入头文件。与C一样的是,如果希望从当前目录中查找头文件, 找不到就到系统的头文件库中查找,对头文件使用双引号(""), 如果只想从系统头文件库中查找,对头文件使用单书名号(<>)。 GNUStep中Objective-C的Foundation头文件目录为:@\GNUstep\System\Library\Headers\Foundation GNUStep中Objective-C的AppKit头文件目录为:@\GNUstep\System\Library\Headers\AppKit 2.static:标识的类变量定义在接口的外面,类变量只能本类访问,除非提供类方法给外部访问这个类变量; 3.@+指令:C之外的Objective-C的衍生语法。@interface表示定义一个接口,接口名后紧跟一个冒号,冒号后是父类名。 Objective-C的顶级父类是NSObject; 4.成员变量:定义在接口内部({}中)。与Java中成员变量同一概念。使用@public,@protected,@private 作为访问修饰符。默认@protected。Objective-C中只有成员变量有访问修饰符,类变量,类方法,成员方法是没有访问修饰符的。 所有的方法都是public的,所有的类变量都是私有的; 5.成员方法和类方法:-开头的方法为成员方法,+开头的方法为类方法。 方法中的类型描述(返回值类型,参数类型)都必须用括号()包围。 如果方法中有多个参数,每个参数都有一个标签名(可以省略,但不建议), 每个标签名后使用冒号与参数类型描述分隔。类方法通常用来提供初始化对象的便捷方法, 且只能由类调用,使用对象调用时会报错; 6.@end:标示接口定义的结束。由于{}只能放置成员变量,因此必须有个结束标志; 7.Objective-C中的@interface与Java中的interface不是同一概念。而@protocol与Java中的interface一样。 @interface只是类的描述,通常在独立的h文件中,可以理解为C中的函数原型,也就是在Objective-C中应叫类原型, 通过这个接口,编译器可以知道具体实现类有哪些功能; 8.setter和getter:Objective-C中以get作为前缀的方法具有特殊的作用,故不能用于getter方法;类的实现:ClassName.m
格式: #import Header @implementation OjbectName -(ReturnType) MethodName:(ParamType) ParamName LabelN:(ParamType) ParamName......{ Statements; ...... } ...... ...... ...... +(ReturnType) MethodName:(ParamType) ParamName LabelN:(ParamType) ParamName......{ Statements; ...... } ...... ...... ...... @end 说明: 1.该类的任务是实现所引用Header中@interface里的方法,因此不能在这里定义类变量或成员变量; 2.在此不必实现@interface中所有方法,也可在此定义@interface中没有声明的方法(因为Objectiv-C是动态语言); 3.当参数名与类属性名一样时,self->PropertyName指类中属性,同Java中this.PropertyName; 4.[supper MethodName:(ParamType)ParamValue LabelN:(ParamType) ParamValue ......]:调用父类中的指定方法,如:self=[supper init]; 4.[self MethodName:(ParamType)ParamValue LabelN:(ParamType) ParamValue ......]:调用自身方法;Category:
类别,用于在不使用继承方式下扩充一个类的功能。一个类可以有多个类别; 实现:在想要扩展的@interface的名字后加(CategoryName),括号内是类别的名字,这个名字必须是唯一的,相应的实现类@implementation的名字后也要加相同的(CategoryName); 类别与继承的区别:不能定义新的成员变量,如果新方法与原始类中的方法同名,原始方法将被隐藏,不能使用super激活原始类中的同名方法;协议:ProtocolName.h
实现: @protocol ProtocolName -(ReturnType) MethodName:(ParamType) ParamValue LabelN:(ParamType) ParamName ......; @end 说明: 1.协议在Objectiv-C中的作用等同于Java中的接口,切允许多继承; 2.协议的实现:ClassName <ProtocolName,ProtocolN,......>; 3.协议类型:<ProtocolName> Val,同id<ProtocolName> Val,相当于Java中使用接口类型作为对象;4.id<ProtocolName,ProtocolNameN,......>:以PotocolNameN作为泛型参数,以明确的告知想把id作为某些协议使用。也可不写泛型参数;
类操作:
1.创建对象: ClassName *ObjectName[[ClassName alloc] init]; Objective-C中实例只能用指针作为变量; 创建对象分两个步骤实现:分配内存[alloc]和初始化[init]; alloc:从NSObject继承而来的类方法,用于给对象分配存储空间。所有的成员变量在此时确定了自己的内存位置,并被赋默认值(INT:0/FLOAT:0.0/BOOL:NO/OBJECT:NIL)并返回对象的指针; init:从NSObject继承而来的成员方法,可以定制自己的init方法。Objective-C对init方法没有特殊的要求,就是一个普通方法而已,只是习惯以init作为前缀,一般都返回当前类型的指针或id类型; 2.调用实例方法: [ObjectPointer MethodName:ParamValue LabelN:ParamValue ......]; 3.调用类方法: [ClassPointer MethodName:ParamValue LabelN:ParamValue ......]; 或: [[ClassName class] MethodName:ParamValue LabelN:ParamValue ......]; 或: Class name=[ClassName class];//因Class已是指针,故name前不需要*; [name MethodName:ParamValue LabelN:ParamValue] 4.使用@public/@protected成员变量: OjbectName->PropertyName; 5.获取Class的几种方法: [Class/Object class]; [Class/Object superclass]; NSClassFromString(StrClass/StrObject);异常:
Object-C异常都继承自NSException; Object-C异常处理:创建NSException异常对象并抛出即可; 创建异常对象: NSException * excp=[ExceptionClass exceptionWithName:@"Name" reason:@"reason" userInfo:nil]; 抛出异常: @throw excp; 捕获异常: @try{ //Statments; } @catch(NSException *excp){ //Statments; } @finally{ //Statments; }内存管理:
Objectiv-C即支持手动管理内存,也支持GC机制,但GC机制仅对MAC OS X有效,对IOS设备无效; Objective-C在使用alloc,new,copy时为对象分配内存,然后返回所分配的内存的首地址,存入指针变量。使用dealloc释放内存。new是alloc和init的合写形式; Objective-C回收内存是通过一个计数器retainCount来表示有多少个地方在引用当前对象。一个对象在被alloc后其retainCount为1,之后每次调用retain方法都会使retainCount加1, 调用release使retainCount减1。当Objectiv-C发现一个对象的retainCount为0时,就会立即调用其从NSObject继承而来的dealloc方法回收内存。该调用动作由Objectiv-C运行环境完成; 内存释放: [ObjectPointer release];对象复制:
对象能否被复制的依据是判断该对象是否遵循NSCopying协议。该协议中有个复制方法: -(id) copyWithZone:(NSZone*) Zone:该方法的调用是由Objecti-C的运行环境来完成的,程序中使用copy方法实现对象的调用。Zone是由系统传进来的一个表示内存空间的NSZone对象,即在该空间内复制原来的对象。复制过程如下: 1.调用类方法allocWithZone传入的NSZone参数,为即将产生的新对象分配空间,该方法必须由[self class]调用; 2.调用初始化方法,完成新实例的创建; 3.把原有实例中的内容都setter到新实例中; ClassName.m实现: -(id) copyWithZone:(NSZone*) zone{ ClassName *obj=[[[self class] allocWithZone:zone] init]; return obj; }多线程:
Objective-C的多线程与Java相似,分为原始的线程操作和线程池两种。线程池是使用队列等机制对原始线程的封装; NSThread:NSThread类型表示线程,其对象的创建如下: -(id) init: -(id) initWithTarget:(id) Target selector:(SEL) Selector object:(id) Argument:第一个参数指定目标对象,一般情况下是当前的实例self。 第二个参数指要运行的方法,相当于Java中的run方法。第三个参数一般为nil; -(void) detachNewThreadSelector:(SEL) Selector toTarget:(id) Target withObject:(id) Argument:该方法与initWithTarget一样,区别就是不需要显示的released这个线程,因为么有使用alloc; -(void) setName:(NSString*) ThreadName:对线程设置名称; -(void) start:启动一个线程; -(void) exit:结束一个线程。使用该方法前要先将其引用计数器归0; +(void) sleepForTimeInterval:(int) TimeSpan:使当前线程暂停指定时间; +(id) currentThread:获取当前正在运行的线程; -(NSString*) name:获取当前线程的名字; NSCondition:执行同步操作,功能和用法上相当于Java中的Lock对象。一段代码如果需要同步就用NSCondation先锁定,需要同步的部分执行完毕再解锁。 NSCondition对象的两个方法lock和unlock分别进行枷锁和解锁操作,将同步执行的代码放于这两个方法之间即可; 此外Objective-C支持@synchronized指令做代码同步: @synchronized(ObjectToLock){ //Statements;同步代码 } 与主线程交互: 子线程不能直接与主线程交互,需要在子线程run方法中调用performSelectorOnMainThread才能实现: -(void) performSelectorOnMainThread:(SEL) Selector withObject:(id) Arg waitUntilDone:(BOOL) Wait; 线程池: Objectiv-C使用NSOperation和NSOperationQueue两个类实现线程池的操作。NSOperation与Java中Runnable相似,其中的main方法就是要执行的操作。 NSOperationQueue是个线程池队列。当把NSoperation添加到NSOperationQueue中时,队列就会先对NSOperation进行remain操作,然后再执行其中的线程操作。 默认情况下队列中的任务串行执行,当NSOperation中的isConcurrent方法返回YES时,队列中的任务就可以并行执行; -(void) setMaxConcurrentOperationCount:(int) Count:设置线程池做大可同时执行的操作数; -(void) addOperation:(NSOperation*) Option:将任务添加到线程池中;KVC && KVO:
KVC是NSKeyValueCoding的缩写,是Foundation Kit中NSObject的一个Category,作用是提供动态访问对象中的属性,类似Java中的反射机制; KVC使用了与NSDicionary相似的key-value的操作方法,即按照按照一个字符串key在对象中查找属性,然后获取或设置其值。如果key为XXX,对于获取属性值的查找规则如下: 1.查找.h文件中声明的成员方法getXXX或XXX; 2.查找.m文件中声明的变量是有成员方法_getXXX或_XXX; 3.查找成员变脸_XXX或XXX; 如getter和setter: 正常情况: [Obj setter:(id) Value];//setter NSLog(@"%@",[Obj getter]);//getter KVC中: [Obj setValue:(id) Value forKey:(id) Property];//setter NSLog(@"%@",[Obj valueForKey:(id) Property];//getter KVC一般用于动态绑定,即才运行时确定调用哪个方法。在解析key的字符串时会比正常的setter,getter慢,而且编译器无法在编译时对方法调用作出检查; KVO是NSKeyValueObserving的缩写,基于KVC和观察者模式实现的一种通知机制。可以类比Java中的JMS,通过定于的方式,实现两个对象间的解耦,但又可以让它们相互调用。 按照观察者模式的订阅机制,KVO必须有如下三个方法: 订阅:Subscribe -(void) anddObserver:(NSObject*) Observer forKeyPath:(NSString*) Path options:(NSKeyValueObservingOptions) Options context:(void*) Context: Options为NSKeyValueObservingOptionOld,NSKeyValueObservingOptionNew; 取消订阅:Unsubxcribe -(void) removeObserver:(NSObject*) Observer forKeyPath:(NSString*) Path; 接收通知:Receive Notification -(void) observeValueForKeyPath:(NSString*) Path ofObject:(id) Object change:(NSDictionary*) Change context:(void*) Context; Notification是Objective-C中的另外一种事件通知机制。NSPredicate:
Cocoa提供NSPredicate谓词,用于指定过滤条件。谓词是指在计算机中表示计算真假值的函数,像SQL中的查询条件,主要用用从集合中分检出符合条件的对象,也可用于字符串的正则匹配; +(NSPredicate*) predicateWithFormat:(NSString*) Format:获取满足条件的对象集合。参数Format和SQL中where语句相似,也可用NSLog的格式化输出模版; -(BOOL) evaluateWithObject:(id) Object:判断对象Object是否满足NSPredicate对象的过滤条件; 相关: 1.逻辑运算符:AND、OR、NOT: 计算并、或、非的结果; 2.范围运算符:BETWEEN、IN: @"pId BETWEEN{1,5}"; @"name IN {'nme1','nme2'}"; 3.占位符: NSPredicate *pdtTmp=[NSPredicate predicateWithFormat:@"name==$NAME"]; NSDicionary *dcr=[NSDicionary dictionaryWithObjectsAndKeys:@"Name1",@"NAME",nil]; NSPredicate *pdt=[pdtTmp predicateWithSubstitutionVariables:dic]; 该例中的占位符就是字典对象中的key,可以使用多个占位符,只要key不同就可以了; 4.快速筛选数组: NSPredicate *pdt=[NSPredicate pridicateWithFormat:@"pId>1"]; NSMutableArray *aryPdt=[aryObj filteredArrayUsingPredicate:pdt]; aryPdt为从数组aryObj中筛选的满足pdt的元素所组成的新数组; 5.字符串运算符: BEGINSWITH、ENDSWITH、CONTAINS:分别表示是否以某字符串开头、结束、和包含。它们可以与c、d联用,表示是否忽略大小写、是否忽略重音字母; @"name BEGINSWITH[cd] 'HE'":判断name是否以He开头,并忽略大小写和重音; 6.LIKE: 使用?表示一个字符,*表示多个字符。也可以与c、d联用; @"name LIKE '???er'":与Paper Plane匹配; 7.SELF: 适用于所匹配的数组元素为字符串或其它没有属性的类型,而非对象; NSArray *ary=[NSArray arrayWithObjects:@"Apple",@"Google",@"Microsoft",nil]; NSPredicate *pdt=[NSPredicate predicateWithFormat:@"SELF=='Apple'"]; 8.正则表达式 NSPredicate使用MATCHES匹配正则表达式, NSString *rgx=@"^A.+e$"; NSPredicate *pdt=[NSPredicate predicateWithFormat:@"SELF MATCHES %@",rgx]; if([pdt evaluateWithObject:@"Apple"]){ printf("YES\n"); } else{ printf("NO\n"); }