1、十分钟让你明白Objective-C的语法(和Java、C++的对比)





深入浅出Objective-c_内存管理




深入浅出Objective-c_内存管理_02





C++:



void helloWorld(bool ishelloworld) {
//干点啥
}



OC:



-(void) HelloWorld:(BOOL)ishelloworld{
//干点啥
}


前面带有减号(-) 的方法为实例方法,必须使用类的实例才可以调用的。对应的有+号, 代表是类的静态方法,不需要实例化即可



调用



[object  message:param1 withParameter:param2]
NSString *string;  
string = [[NSString alloc] initWithString:@"Hello"];




三、Import




四 、Property 和Synthesize 


Property定义:@property 声明用于自动创建property属性变量的getter和setter


Synthesize定义:@Synthesize声明实现了property属性变量的getter和setter。


例子:


在  interface:@property dataType variableName


在  implementation:  synthesiz variableName



五、头文件中的方法



-(returnType)method
-(returnType)method:(dataType)param1
-(returnType)method:(dataType)param1 withParam:(dataType)param2



类似于:C/C++/Java



returnType method()
returnType method(param1)
returnType method(param1,param2)



指向自己的指针


[self method]


类似于:c++/java


this.method();



七、继承关系和接口实现


ClassA:ParentA  
ClassA:ParentA<Protocol>  
ClassA <Protocol>
ClassA extends ParentA  
ClassA extends ParentA implements interface  
ClassA implements interface

八、空指针


id obj = nil;
NSString *hello = nil;


nil相当与Java中的null;



九、 id


objective-c的和C++里的(void*)类似


在iOS系统里,Objective-C的内存需要自己管理,添加了ARC机制后编译器帮助了Objective-C  添加release释放的代码。而Java是通过垃圾回收器管理内存的。




2、iOS系统(简介,语法,系统结构)


autoreleasepool这个是做什么用的呢?


      简单的说,每次事件处理时候开始的时候,ios会为我们自动生成一个autorelesepool,结束的时候释放掉。


对于每一个Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。


       那什么是一个Runloop呢? 


一个UI事件,Timer call, delegate call, 都会是一个新的Runloop。Autorelease是保证一个method安全的,对于method中的函数调用也适用



1、IOS的系统结构


1、核心操作系统层, Core OS 


2、核心服务层 Core Services  


3 、媒体层 Media 


4、Cocoa Touch层




2 OC语言的基本数据类型长度:同C/C++相同




3、格式化输出数据 类似




4、NSInteger是基础类型,但是NSNumber是一个类



5、NSString与NSInteger的相互转换


   NSString * string = [NSString stringWithFormat:@"%d",integerNumber];


   integer = [string intValue]; 



3、字符串NSString



NSString和NSMutableString:NSString创建赋值后不能动态修改长度和内容,除非给重新赋值。


而NSMutableString类似与链表的,在创建赋值后可以进行修改长度,插入,删除等操作。



1、创建常量字符串。


NSString *astring = @"我是字符串";



2、NSString的内存地址


对比的是内存地址,存储在文字常量区,string1和string2都指向这个字符串,所以指向的是同一个地址


NSString *bstring = @"我是字符串";   


NSString *astring = [[NSString alloc] init]; 


astring=@"我是字符串";  


开始不一样,重新赋值后,astring 的地址和bstring地址一样了



3、NSString字符串的比较


isEqualTostring/compare返回整数-1,0,1


BOOL result = [string1 compare:string2];



4、字符串分割:


NSString *nstring = @"美国,加拿大,澳大利亚,津巴布韦,埃及";  


NSArray *array = [nstring componentsSeparatedByString:@","];  



5、创建格式化字符串


NSString *astring = [[NSString alloc] initWithString:[NSString stringWithFormat:@"%d.这是第 %i 字符串",a,b]]; 



6、用标准c创建字符串:initWithUTF8String方法


char *Cstring = "我是字符串"; 


NSString *astring = [[NSString alloc] initWithUTF8String:Cstring]; 



7、在串中搜索子串和替换


hasPrefixe 匹配字符串头


haSuffix     匹配字符串的尾巴


NSRange rang = [string rangeOfString:temp];  


//将搜索中的字符串替换成为一个新的字符串  


NSString *str = [string stringByReplacingCharactersInRange:rang withString:@"大产"];  


//将字符串中" " 全部替换成 *  


str = [string stringByReplacingOccurrencesOfString :@" " withString:@"*"];  


NSLog(@"替换后字符串为%@", str); 




4、Objective-C语法之类和对象


声明了一个叫做 MyClass 的类,它继承于根类:NSObject



深入浅出Objective-c_objective-c_03




-  public;


+ 静态的,不用实例化即可访问;



方法的声明由以下几个部分构成:方法类型标识符,返回类型,一个或多个方法签名关键字,以及参数类型和名称



深入浅出Objective-c_Java_04





@interface Worker : NSObject
{
    char *name;
@private
    int age;
    char *evaluation;
@protected
    id job;
    float wage;
@public
    id boss;
}


默认是protected的



2、创建类


在头文件里添加类成员变量和方法


#import <Foundation/Foundation.h>
@interface Student : NSObject
{
    NSString *studentName;
    NSInteger age;
}


-(void) printInfo;
-(void) setStudentName: (NSString*) name;
-(void) setAge: (NSInteger) age;
-(NSString*) studentName;
-(NSInteger) age;
@end


  • @interface 类的开始的标识符号 ,好比Java  或 C 语言中的Class   
  • @end 类的结束符号
  • 继承类的方式:Class:Parent,如上代码Student:NSObject
  • 成员变量在@interface Class: Parent { .... }之间
  • 成员变量默认的访问权限是protected。
  • 类成员方法在成员变量后面,格式是:: scope (returnType) methodName: (parameter1Type) parameter1Name;
  • scope指得是类方法或实例化方法。类方法用+号开始,实例化方法用 -号开始。



实现类中的方法


#import "Student.h"

@implementation Student

-(void) printInfo
{
    NSLog(@"姓名:%@ 年龄:%d岁",studentName,studentAge);
}
-(void) setAge: (NSInteger) age
{
    studentAge = age;
}
-(NSInteger) age
{
    return studentAge;
}
@end



创建类对象,调用方法。


Student *student = [[Student alloc]init];
    [student setStudentName:@"张三"];
    [student setAge:10];
    [student printInfo];
    [student release];
  •  [Student alloc]调用Student的类方法,这类似于分配内存,
  •  [object init]是构成函数调用,初始类对象的成员变量。


2、类的实例方法使用多个参数



-(void) setNameAndAge:(NSString*) name setAge:(NSInteger) age;
-(void) setNameAndAge:(NSString*) name setAge:(NSInteger) age
{
    studentName = name;
    studentAge = age;
}
[student setNameAndAge:@"李四" setAge:20];




3、自定义构造函数



-(Student*) initWithNameAndAge:(NSString*) name setAge:(NSInteger) age; 
-(Student*) initWithNameAndAge:(NSString*) name setAge:(NSInteger) age
{
    self = [super init];
   
    if ( self ) {
        [self setNameAndAge:name setAge:age];
    }
   
    return self;
}


每一个类在创建的时候需要调用init方法,使用父类的init 方法得到了self,这就可以做一些子类初始化的工作



5、Objective-C语法之异常处理


@try {
        <#statements#>
    }
    @catch (NSException *exception) {
        <#handler#>
    }
    @finally {
        <#statements#>
    }



1、新建SomethingException,SomeOverException这两个类,都继承与NSException类


@interface SomethingException : NSException
@end

@implementation SomethingException
@end




2、新建Box类,在某些条件下产生异常。



#import <Foundation/Foundation.h>
@interface Box : NSObject
{
    NSInteger number;
}
-(void) setNumber: (NSInteger) num;
-(void) pushIn;
-(void) pullOut;
-(void) printNumber;
@end


@implementation Box
-(id) init {
    self = [super init];
   
    if ( self ) {
        [self setNumber: 0];
    }
   
    return self;
}

-(void) setNumber: (NSInteger) num {
    number = num;
   
    if ( number >= 6 ) {
        // throw warning
        NSException *e = [SomethingException
                          exceptionWithName: @"BoxWarningException"
                          reason: @"The level is above or at 60"
                          userInfo: nil];
        @throw e;
    } else if ( number < 0 ) {
        // throw exception
        NSException *e = [NSException
                          exceptionWithName: @"BoxUnderflowException"
                          reason: @"The level is below 0"
                          userInfo: nil];
        @throw e;
    }
}

-(void) pushIn {
    [self setNumber: number + 1];
}

-(void) pullOut {
    [self setNumber: number - 1];
}

-(void) printNumber {
    NSLog(@"Box number is: %d", number);
}
@end


初始化Box时,number数字是0,可以用pushIn 方法往Box里推入数字,每调用一次,number加1.当number数字大于等于6时产生SomethingException异常,告诉你数字达到或超过6了,超过10时产生SomeOverException异常,小于1时产生普通的NSException异常。


这里写 [ SomeOverException   exceptionWithName : @"BoxOverflowException"   reason :@"The level is above 100"异常的名称和理由,在捕获时可以获取。



超过6  Terminating app due to uncaught exception  'BoxWarningException' , reason:  'The number is above or at 60'   





6 、 Objective-C语法之动态类型(isKindOfClass, isMemberOfClass,id)等


对象在运行时获取其类型的能力称为内省。内省可以有多种方法实现。

判断对象类型

-(BOOL) isKindOfClass: classObj判断是否是这个类或者这个类的子类的实例

-(BOOL) isMemberOfClass: classObj 判断是否是这个类的实例



-(BOOL) respondsToSelector: selector  判读实例是否有这样方法

+(BOOL) instancesRespondToSelector:  判断类是否有这个方法。此方法是类方法,不能用在类的对象


Objective-C的id类型


在 Objective-C 中,id类型类似于(void*) ,可以指向任何类的实例。而不需要强制转换


7、Objective-C语法之内存管理



1、Objective-C内存管理简介:


Objective-C需要自己考虑内存的管理, 介于C/C++和Java C#直接,不像C/C++语言内存管理全部需要程序员一手包办,也不像Java C#语言有那么完备的内存垃圾回收器。


在iOS 5后增加了Automatic Reference Counting(ARC 自动引用计数)特性,这样程序员不需要自己操心管理内存了,ARC和GC不一样,ARC是编译器的行为。



2、内存管理原理:


 Objective-C内存管理模型是基于对象的所有权。如果你拥有这个对象,那么你就有责任去释放它。一个对象可以有多个拥有者。如果这个对象的拥有和为0时,系统将自动释放这个对象。


对象的所有权和释放有四个原则:


  • 任何你创建的对象你都获得其所有权。(包括 alloc ,new ,copy等关键字获得的对象)
  • 通过retain获得对象的所有权
  • 如果你不需要一个对象了,你必须释放所有权
  • 你不能释放你没有所有权的对象


引用计数(retainCount)是Objective-C对象引用的唯一依据。调用实例管理的release方法后,此属性减1,减到为零时对象的dealloc方法被自动调用,进行内存回收操作,也就是说我们永不该手动调用对象的dealloc方法。



深入浅出Objective-c_内存管理_05



3、示例:环境是在不选择ARC的环境下



新建Person类,使他继承与NSObject,在.m文件中实现dealloc方法:


- (void) dealloc 
{ 
    NSLog (@"dealloc called. Bye Bye."); 
    [super dealloc]; 
   
}



在引用计数为0时,这个方法就会被调用,证明这个对象被销毁。


创建一个对象,打印它的引用计数


Person *person = [[Person alloc] init];
    NSLog(@"对象person的retainCount: %d", [person retainCount]);
//对象person的retainCount: 1

   Person *person = [[Person alloc] init];
    NSLog(@"对象person的retainCount: %d",[person retainCount] );
    [person retain];
    NSLog(@"对象person的retainCount: %d", [person retainCount]);


打印结果:

对象person的retainCount: 1

对象person的retainCount: 2


和传说的一样,retainCount增加了。



在release时,减少到1时就不再减 了


Person *person = [[Person alloc] init];
    NSLog(@"对象person的retainCount: %d",[person retainCount] );
    [person retain];
    NSLog(@"对象person的retainCount: %d", [person retainCount]);
    [person release];
    [person release];
    NSLog(@"对象person的retainCount: %d", [person retainCount]);


2012-07-05 16:05:29.830 ObjectiveCTest[2847:f803] 对象person的retainCount: 1
2012-07-05 16:05:29.831 ObjectiveCTest[2847:f803] 对象person的retainCount: 2
2012-07-05 16:05:29.831 ObjectiveCTest[2847:f803] dealloc called. Bye Bye.
2012-07-05 16:05:29.832 ObjectiveCTest[2847:f803] 对象person的retainCount: 1


第一次release 时 retainCount减1了 ,再release , d对象的dealloc called了,但是retainCount 还是1?


// 管理内存是,不要看这个方法,没啥用。




4、NSAutoreleasePool自动释放内存



程序入口程序,也用到自动释放。


NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];


我们先把pool看成一个普通对象,先是alloc,pool的retainCount为1。
第三句release,retainCount为0,自动调用它的dealloc方法。它和任何其它普通对象没 任何区别



NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count为1,那么release之后,retain count为0,


NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Person *person = [[Person alloc] init];
    NSLog(@"对象person的retainCount: %d",[person retainCount] );
    [person autorelease];
    NSLog(@"到这里,person还没被释放");
    [pool release];


打印结果:


2012-07-05 16:34:38.823 ObjectiveCTest[3045:f803] 对象person的retainCount: 1
2012-07-05 16:34:38.823 ObjectiveCTest[3045:f803] 到这里,person还没被释放
2012-07-05 16:34:38.824 ObjectiveCTest[3045:f803] dealloc called. Bye Bye.


在调用pool release的时候,person才被释放。这就是自动释放的神奇的地方,这样就可以创建对象




8、 Objective-C语法之NSArray和NSMutableArray



NSArray保存的对象可以是不同的对象。但只能保存对象,int ,char,double等基本数据类型不能直接保存,需要通过转换成对象才能加入数组。




1、NSArray 不可变数组


[array count] : 数组的长度。
[array objectAtIndex 0]: 传入数组脚标的id 得到数据对象。
[arrayWithObjects; ...] :向数组对象初始化赋值。这里可以写任意对象的指针,结尾必须使用nil。


#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc]init];       
        NSArray *array = [NSArray arrayWithObjects:
                                   @"a",
                                    obj,
                                   @"c",nil];
        NSLog(@"array Count:%lu",[array count]);
        //遍历数组
        for (NSObject *object in array) {
            NSLog(@"数组对象:%@", object);
        }
        [obj release];
    }
    return 0;
}



2、NSMutableArray可变对象数组


[NSMutableArray arrayWithCapacity:6] :初始化可变数组对象的长度,如果后面代码继续添加数组超过长度6以后NSMutableArray的长度会自动扩充,6是自己可以设置的颗粒度。
[array addObject:...] : 向可变数组尾部添加数据对象。
[array addObjectsFromArray:..] :向可变数组尾部添加一个数组对象。


#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc]init];  
        NSMutableArray *muArray = [NSMutableArray arrayWithCapacity:6];
        [muArray addObject:@"对象1"];
        [muArray addObject:@"对象2"];
        [muArray addObject:@"对象3"];
        [muArray addObject:@"对象4"];
        [muArray insertObject:@"搅局的" atIndex:2];
        [muArray addObject:obj];
        for (NSObject * object in muArray) {
            NSLog(@"数组对象:%@", object);
        }   
        [obj release];
    }
    return 0;
}


数组对象:<NSObject:  0x109714110 >


NSRange range = NSMakeRange(0,3); 设置一个范围为 0 到 3 之间。
[array removeObject:obj inRange:range] : 设置在一个范围内删除数据,如果这个范围内没有删除的这个对象则不会删除任何东西。例子中因为obj对象在 数组 0 到 3的范围内,所以obj就被删除掉了。


[array removeObject:(id)] :删除数组中指定元素,根据对象isEqual消息判断。


[array removeObjectIdenticalTo:(id)] : 删除数组中指定元素,根据对象的地址判断


[muArray replaceObjectAtIndex:4 withObject:@ "字符串替换回来" ];  //


替换某索引值对应的对象



3、数组迭代的遍历方法


NSMutableArray *muArray = [NSMutableArray arrayWithCapacity:6];
        [muArray addObject:@"对象1"];        
        NSEnumerator *enmuerator = [muArray objectEnumerator];
        id object;
        while (object = [enmuerator nextObject]) {
            NSLog(@"数组中的对象:%@", object); 
       }







9、Objective-C语法之NSDictionary和NSMutableDictionary




与Map类似,可以把数据以键值对的形式储存起来,在Objective-C语言中,词典就是做这样的事情的,和NSArray一样,一个词典对象也能保存不同类型的值,词典也分别有不可变词典和可变的词典(NSDictionary与NSMutableDictionary),前者是线程安全的,后者不是 。



1、不可变词典NSDictionary的主要用法:

[NSDictionary dictionaryWithObjectsAndKeys:..] : 使用键值对直接创建词典对象,结尾必需使用nil标志结束。

[dictionary count]: 得到词典的键值对数量。
[dictionary keyEnumerator]: 将词典的所有key储存在NSEnumerator中,类似于Java语言中的迭代器
[dictionary objectEnumerator]: 将词典的所有value储存在NSEnumerator中
[dictionary objectForKey:key]: 通过传入key对象可以拿到当前key对应储存的值。


int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"25",@"age",@"张三",@"name",@"男",@"性别",nil];
        NSLog(@"%lu", [dictionary count]);
        NSEnumerator *enumeratorKey = [dictionary keyEnumerator];
        for (NSObject *object in enumeratorKey) {
            NSLog(@"key:%@", object);
        }
       
        NSEnumerator *enumeratorObject = [dictionary objectEnumerator];
        for (NSObject *object in enumeratorObject) {
            NSLog(@"value:%@", object);
        }
        NSLog(@"key name的值是:%@", [dictionary objectForKey:@"name"]);
       
    }
    return 0;
}



3


key:age


key:name


key:性别


value:25


value:张三


value:男


key name的值是:张三







2、可变的词典NSMutableDictionary。

NSMutableDictionary是NSDictionary的子类,所以继承了NSDictionary的方法, 以上的代码对NSMutableDictionary来说完全可用。我们试试不一样的地方

增删键值数据。


[dictionary setObject: forKey:] :向可变的词典动态的添加数据 
[dictionary removeAllObjects..] : 删除掉词典中的所有数据。
[dictionary removeObjectForKey..] :删除掉词典中指定key的数据


int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"25",@"age",@"张三",@"name",@"男",@"性别",nil];
        [dictionary setObject:@"30名" forKey:@"名次"];
       
        NSLog(@"%lu", [dictionary count]);
        NSEnumerator *enumeratorKey = [dictionary keyEnumerator];
        for (NSObject *object in enumeratorKey) {
            NSLog(@"key:%@", object);
        }
       
        NSEnumerator *enumeratorObject = [dictionary objectEnumerator];
        for (NSObject *object in enumeratorObject) {
            NSLog(@"value:%@", object);
        }
        NSLog(@"key 名次的值是:%@", [dictionary objectForKey:@"名次"]);
        [dictionary removeObjectForKey:@"名词"];
        NSLog(@"%lu", [dictionary count]);
    }
    return 0;
}


4key:agekey:性别


key:name


key :名次 value:25value:张三value:男


value : 3 0 名key 名次的值是:30名




 


10、Objective-C语法之NSSet和NSMutableSet





NSSet和NSMutableSet是无序的, 但是它保证数据的唯一性。当插入相同的数据时,不会有任何效果。从内部实现来说是hash表,所以可以常数时间内查找一个数据。




1、NSSet的使用

[NSSet setWithSet:(NSSet *)set]; 用另外一个set对象构造

[NSSet setWithArray:(NSArray *)array];用数组构造

[NSSet setWithObjects:...]:创建集合对象,并且初始化集合中的数值,结尾必需使用nil标志。

[set count] ; 得到这个结合对象的长度。

[set containsObject:...]: 判断这个集合中是否存在传入的对象,返回Bool值。

[set objectEnumerator]: 将集合放入迭代器。

[enumerator nextObject]:得到迭代器中的下一个节点数据,使用while遍历这个迭代器,方可遍历集合对象中的对象。

[set isEqualToSet:objset]:判断两个集合是否完全相等,返回Bool值。

[set isSubsetOfSet:objset]:判断集合中的所有数据是否都相等与objeset集合中,返回Bool值。

[set allObjects];


1.1 以NSArray构造set


NSArray *array = [[NSArray alloc] initWithObjects:@"对象abc",@"rongfzh", @"totogo2010",nil];NSSet *set3 = [NSSet setWithArray:array];NSLog(@"%@", set3);




1.2 set的一些比较方法的使用


int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSSet *set = [NSSet setWithObjects:@"25",@"age",@"张三",@"name",@"男",nil];
        NSSet *set1 = [NSSet setWithObjects:@"25",@"age",@"张三",@"name",@"男",@"性别",nil];
       
        NSLog(@"set count:%lu", [set count]);
        //判断是否含有age字符串
        if([set containsObject:@"age"]) {
            NSLog(@"set包含age");
        }
        //判断set 是否等于set1
        if ([set isEqualToSet:set1]) {
            NSLog(@"set 等于 set1");
        }
        //判断set是否是否是set1的子集合
        if ([set isSubsetOfSet:set1]) {
            NSLog(@"set isSubsetOfSet set1");
        }
        //获取所有set对象
        NSArray *array = [set allObjects];
        NSLog(@"array:%@", array);
       
        //迭代遍历
        NSEnumerator *enumerator = [set objectEnumerator];
        for (NSObject *object in enumerator) {
            NSLog(@"set1里的对象:%@", object);
        }
    }
    return 0;
}


set count:5 


set包含age


set isSubsetOfSet set1 


array :( 


age,  


2 5 ,  


"\U7537" ,  


"\U5f20\U4e09" ,  


name 


)  


set 1 里的对象:age


...






2、NSMutableSet的使用

NSMutableSet继承NSSet,它可以使用NSSet的方法。


[NSMutableSet setWithCapacity:6]:创建可变集合对象,并且初始化长度为6。
[set addObject: obj] : 向集合中动态的添加对象。
[set removeObject:obj]:删除集合中的一个对象。
[set removeAllObjects]:删除集合中的所有对象。
[set unionSet:obj]:向集合中添加一个obj集合的所有数据。
[set minusSet:obj]:向集合中删除一个obj集合的所有数据。
[set intersectSet]:向集合中删除一个不包含obj集合的所有数据。




int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSMutableSet *muSet = [NSMutableSet setWithCapacity:6];
        [muSet addObject:@"对象1"];
        NSSet *set = [NSSet setWithObjects:@"对象2",@"对象3", @"被企鹅咬了一口", nil];
        //添加set数据
        [muSet unionSet:set];
        for (NSObject *object in muSet) {
            NSLog(@"all nuSet:%@",object);
        }
        NSSet *set1 = [NSSet setWithObjects:@"对象2",@"对象3", nil];
       
        //在muSet中删除包含set1总数据
        [muSet minusSet:set1];
        for (NSObject *object in muSet) {
            NSLog(@"after minusSet:%@",object);
        }
           
    }
    return 0;
}


all nuSet:对象1

all nuSet:被企鹅咬了一口

all nuSet:对象2

all nuSet:对象3

after minusSet:对象1

after minusSet:被企鹅咬了一口









11、Objective-C语法property详解




1、简介: 


property是Objective-C的关键词,与@synthesize配对使用,用来让编译好器自动生成与数据成员同名的方法声明。@synthesize则是用来生成对应声明方法的实现。



1.1 property的语法格式:

@property (参数1,参数2)类型名字;


/*在@property() 括号中,可以填写的属性:
 * readwrite: 默认,可读可写
 * readonly: 只读
 * assign: 默认,引用计数器不增加
 * retain: 引用计数器加1
 * automic: 默认,原子性,指的是在执行该标记标识的方法时,不会被另外的线程打断
 * nonatomic: 非原子性
 */


1.2 三种方式的使用



assign/retain/copy  代表赋值的方式。




readonly关键字代表setter不会被生成, 所以它不可以和 copy/retain/assign组合使用。



atomicity的默认值是atomic,读取函数为原子操作。




1.2.1 


copy/reain/assign 在其中选择一个来确定属性的setter如何处理这个属性。NSObject对象采用这个中方式。




1.2.2 


一些特别的Object比如NSSstring使用copy。




1.2.3


 assign关键字代表setter直接赋值,而不是复制或者保留它。适用于基本数据类型,比如NSInteger和CGFloat,或者你并不直接拥有的类型,比如delegates。




2、如何使用property

1.1  没有property和有property的对比



在头文件定义 obj。在.m文件中使用


#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
    NSObject *obj;
}
@end

- (void)viewDidLoad
{
    [super viewDidLoad];   
    self.obj = nil;、
}


深入浅出Objective-c_objective-c_06




加上property;


#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    NSObject *obj;
}
@property (nonatomic,retain) NSObject *obj;
@end


编译能通过,运行,崩溃,提示错误  reason: '-[ViewController setObj:]: unrecognized selector sent to instance 0x6b6c480

那就是我们没事实现setter方法。


用@synthesize关键字实现getter 和setter。


@implementation ViewController
@synthesize obj;
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.obj = nil;
}




3、@property和@synthesize关键字 生成的代码


把这两个关键字对应的代码注释掉


#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    NSObject *obj;
}
//@property (nonatomic,retain) NSObject *obj;
-(NSObject*)obj;
-(void)setObj:(NSObject*)newObj;
@end



@implementation ViewController
//@synthesize obj;
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.obj = nil;
}

-(NSObject*)obj{
    return obj;
}
-(void)setObj:(NSObject*)newObj{
    if(obj != newObj){
        [obj release];
        obj = [newObj retain];
    }
}


再运行,也能正常启动。说明自己写的getter 和setter替代了property。



4、使用三种参数的对比




@property (nonatomic,retain)NSObject *obj;
@property (nonatomic,retain,readwrite) NSObject *obj;


readwrite是默认行为,所以这两行代码等价




@property (retain) NSObject *obj;
@property (atomic,retain) NSObject *obj;

atomic是默认行为,所以这两行代码是等价的。



@property(atomic,assign)int number;        
@property(atomic) int number;        
@property int number;


对int 来说,atomic assign都是默认行为,所以这三行是等价的。




@property  NSObject *obj;这样写行吗?不行的,报警告


只有int 等基础数据类型能这么写。对象必须加上赋值的类型。

@property  (retain) NSObject *obj;这样就没问题了。何时使用assign、何时使用retain、copy后面再讲。




5、retain和copy实验。


#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
    NSString *string;
}
@property  (nonatomic, copy) NSString *string;
@end




@synthesize string;
- (void)viewDidLoad
{
    [super viewDidLoad];
   
    NSString *str = [[NSString alloc] initWithFormat:@"abcd"];
    NSLog(@"str_Point:%p  %@  retainCount:%d", str, str, [str retainCount]);
    self.string = str;
    NSLog(@"string_Point:%p  %@  retainCount:%d", string, string, [string retainCount]);
}


打印结果

2012-07-19 20:41:44.853 TestProject1[1213:f803] str_Point:0x6a8e0b0  abcd  retainCount:1

2012-07-19 20:41:44.854 TestProject1[1213:f803] string_Point:0x6a8e0b0  abcd  retainCount:2


内存地址是一样的,不是想其他文字所写的那样,拷贝了一份内存,这里用copy也是浅拷贝。retain也+1




#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    NSString *string;
}
@property  (nonatomic, retain) NSString *string;
@end


打印结果是:

2012-07-19 20:42:08.113 TestProject1[1230:f803] str_Point:0x6d3b8f0  abcd  retainCount:1

2012-07-19 20:42:08.114 TestProject1[1230:f803] string_Point:0x6d3b8f0  abcd  retainCount:2


结果和上面copy一样。



在IOS5之后,加入了Automatic Reference Counting (ARC), iOS5中新加了关键字有 strong ,  weak , unsafe_unretained。




property 中的strong 与weak


strong关键字与retain关似,用了它,引用计数自动+1,用实例更能说明一切


@property (nonatomic, strong) NSString *string1;   

@property (nonatomic, strong) NSString *string2;  

有这样两个属性,

@synthesize string1;   

@synthesize string2;  

self.string1 = @"String 1";   

self.string2 = self.string1;   

self.string1 = nil;  

NSLog(@"String 2 = %@", self.string2);


结果是:String 2 = String 1
由于string2是strong定义的属性,所以引用计数+1,使得它们所指向的值都是@"String 1", 如果你对retain熟悉的话,这理解并不难。



接着我们来看weak关键字:


结果是:String 2 = null


由于self.string1与self.string2指向同一地址,且string2没有retain内存地址,而self.string1=nil释放了内存,所以string1为nil。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。在c/c++开发过程中,为何大牛都说指针的空间释放了后,都要将指针赋为NULL. 在这儿用weak关键字帮我们做了这一步。




12、Objective-C语法之KVC的使用



除了一般的赋值和取值的方法,我们还可以用Key-Value-Coding(KVC)键值编码来访问你要存取的类的属性。

下图来自苹果官网:


深入浅出Objective-c_objective-c_07

如何使用KVC存取对象属性呢?看个示例



1、使用KVC

定义一个Student类,继承于NSObject。


.h文件


#import <Foundation/Foundation.h>

@interface Student : NSObject
{
    NSString *name;
}
@end

#import "Student.h"
@implementation Student
@end



.m文件也没有实现。name属性没有加property,原来的访问方法就访问不了name属性了


#import "Student.h"
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Student *student = [[[Student alloc]init ]autorelease];
        [student setValue:@"张三" forKey:@"name"];
        NSString *name = [student valueForKey:@"name"];
        NSLog(@"学生姓名:%@",name);
    }
    return 0;
}


学生姓名:张三


张三 这个值存进去了,通过valueForKey取出来了。




2、键路径访问属性


如果访问这个类里中的属性中的属性呢?那就用到了键路径 

关键字:键路径取值valueForKeyPath 键路径存值:forKeyPath

新建一个类Course,课程类,课程类有课程名称这个属性


.h文件


#import <Foundation/Foundation.h>

@interface Course : NSObject
{
    NSString *CourseName;
}
@end

#import "Student.h"
@implementation Student
@end



在Student中添加Course属性 ,student.h文件中代码如下:


#import <Foundation/Foundation.h>
@class Course;
@interface Student : NSObject
{
    NSString *name;
    Course *course;
}
@end



在main方法中,我们实验通过键路径访问Course中CourseName的属性


#import "Student.h"
#import "Course.h"
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Student *student = [[[Student alloc]init ]autorelease];
        [student setValue:@"张三" forKey:@"name"];
        NSString *name = [student valueForKey:@"name"];
        NSLog(@"学生姓名:%@",name);
       
        Course *course = [[[Course alloc]init] autorelease];
        [course setValue:@"语文课" forKey:@"CourseName"];
        [student setValue:course forKey:@"course"];
        NSString *courseName = [student valueForKeyPath:@"course.CourseName"];
        NSLog(@"课程名称:%@", courseName);
       
        //也可以这样存值
        [student setValue:@"数学课" forKeyPath:@"course.CourseName"];
        courseName = [student valueForKeyPath:@"course.CourseName"];
        NSLog(@"课程名称:%@", courseName);
       
    }
    return 0;
}


学生姓名:张三


课程名称:语文课


课程名称:数学课




3、自动封装基本数据类型

我们在Student类中添加分数属性 NSInteger point;


 NSInteger point; 


[student setValue:@ "88"  forKeyPath:@ "point" ];  


NSString *point = [student valueForKey:@ "point" ];   NSLog(@ "分数:%@" , point);  




4、操作集合


在Student类中加入数组NSArray,用来表示其他的学生。这样我们可以添加多个其他的学生,再用集合操作计算学生的分数,最高分,最低分,平均分.


NSArray *otherStudent;  
Student *student1 = [[[Student alloc]init]autorelease];
        Student *student2 = [[[Student alloc]init]autorelease];
        Student *student3 = [[[Student alloc]init]autorelease];
        [student1 setValue:@"65" forKey:@"point"];
        [student2 setValue:@"77" forKey:@"point"];
        [student3 setValue:@"99" forKey:@"point"];
        NSArray *array = [NSArray arrayWithObjects:student1,student2,student3,nil];
        [student setValue:array forKey:@"otherStudent"];
        NSLog(@"其他学生的成绩%@", [student valueForKeyPath:@"otherStudent.point"]);
        NSLog(@"共%@个学生", [student valueForKeyPath:@"otherStudent.@count"]);
        NSLog(@"最高成绩:%@", [student valueForKeyPath:@"otherStudent.@max.point"]);
        NSLog(@"最低成绩:%@", [student valueForKeyPath:@"otherStudent.@min.point"]);
        NSLog(@"平均成绩:%@", [student valueForKeyPath:@"otherStudent.@avg.point"]);



13  Objective-C语法之KVO的使用



上篇我们讲到了KVC,这篇我们学习KVO,全名为:Key Value Observing,直译为:基于键值的观察者。

那它有什么用呢?KVO主要用于视图交互方面,比如界面的某些数据变化了,界面的显示也跟着需要变化,那就要建立数据和界面的关联。

ObjC中提供的KVO就是解决这种问题的。以下用显示页面观察学生的课程名称变化的例子来说明KVO的使用。


学生类命名为:Student,页面类是:PageView.



 14 

Objective-C语法之代码块(block)的使用



代码块本质上是和其他变量类似。不同的是,代码块存储的数据是一个函数体。使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值。

脱字符(^)是块的语法标记。按照我们熟悉的参数语法规约所定义的返回值以及块的主体(也就是可以执行的代码)。下图是如何把块变量赋值给一个变量的语法讲解:


深入浅出Objective-c_objective-c_08


按照调用函数的方式调用块对象变量就可以了:
int result = myBlock(4); // result是 28



15  Objective-C语法之Category的使用


无论一个类设计的如何完美,都不可避免的会遇到没有预测到的需求,那怎么扩展现有的类呢?当然,继承是个不错的选择。但是Objective-C提供了一种特别的方式来扩展类,叫Catagory,可以动态的为已经存在的类添加新的行为。这样可以保证类的原原来的基础上,较小的改动就可以增加需要的功能。使用Category对类进行扩展时,不需要访问其源代码,也不需要创建子类,这样我们可以扩展系统提供的类。Category使用简单的方式,实现了类的相关方法的模块化,把不同的类方法分配到不同的分类文件中。




现在我们有一个类叫MyClass


#import <Foundation/Foundation.h>

@interface MyClass : NSObject
-(void) myPrint;
@end

#import "MyClass.h"

@implementation MyClass
-(void) myPrint{
    NSLog(@"myPrint 调用了");
}
@end



我们要在不增加子类,不修改MyClass类的情况下增加一个HelloWorld的方法,怎么添加呢?只需添加两个文件MyClass+HelloWorld.h  和 MyClass+HelloWorld.m。


深入浅出Objective-c_objective-c_09

Category on的类是MyClass,选对了哦


深入浅出Objective-c_字符串_10

这样Xcode就帮你创建了MyClass+HelloWorld.h  和 MyClass+HelloWorld.m这两个 文件了。


那么我们现在添加一个HelloWorld方法。看看实现后的代码如下:


#import "MyClass.h"

@interface MyClass (HelloWorld)
-(void)HelloWorld;
@end

#import "MyClass+HelloWorld.h"

@implementation MyClass (HelloWorld)
-(void)HelloWorld{
    NSLog(@"你好 伦敦奥运!");
}
@end



在main中调用


MyClass *myclass = [[[MyClass alloc]init]autorelease];
        [myclass HelloWorld];
        [myclass myPrint];


你好 伦敦奥运!


myPrint 调用了  




那的Category的使用场景有那些呢:
1、类包含了很多个方法实现,而这些方法需要不同团队的成员来实现
2、当你在使用基础类库中的类时,你不想继承这些类而只想添加一些方法时。
 
Category能实现上面的需求,当然也有使用Category是需要注意的问题:
1、Category可以访问原始类的实例变量,但不能添加实例变量,如果想添加变量,那就通过继承创建子类来实现。
2、Category可以重载原始类的方法,不大不推荐这么做,这样会覆盖掉原始类的方法。如果确实要重载,那就通过继承创建子类来实现。
3、和普通接口有所区别的是,在Category的实现文件中的实例方法只要你不去调用它你可以不用实现所有声明的所有方法。




16  Objective-C新语法特性


1、枚举类型的改变


typedef enum Week:NSUInteger{
    Moday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday   
}Week;


新方法在列出枚举内容的同时绑定了枚举数据类型NSUInteger,这样带来的好处是增强的类型检查和更好的代码可读性。




2、使用的方法代码放置的位置顺序无关



没在.h文件中声明的方法,在时候的时候如果方法不在前面,可能会有警告。


新的编译器会先扫描代码中的方法,然后再编译,这样就避免了找不到方法这种情况了




3、property属性简化



@property对于使用Objective-C的程序员来说是相当熟悉的,property方便自动生成变量的getter 和setter。在.h文件中声明之后,还要在.m文件中加上@synthesize关键字,这样才能完成自动getter 和setter的过程。


比如说,我在.h文件中写了

@property (strong, nonatomic) NSDictionary *order;

我还要去对于的.m文件中写上

@synthesize order;

是不是感觉很多余啊?



现在在语法新特性中不用写这行代码了,新版的编译器帮你实现这行代码,这叫帮人帮到底。


也是说,你在.h文件中声明order属性后,就可以直接在实现文件中使用该属性的getter和setter方法,编译器还会根据属性的可读和可写自动判断是否提供setter方法。智能多了。



4、语法的简化