OC的特有语法-分类Category、 类的本质、description方法、SEL、NSLog输出增强、点语法、变量作用域、@property @synthesize关键字、Id、OC语言构造方法
一、 分类-Category
@interface 类名 (分类名称)
// 方法声明
@end
@implementation 类名 (分类名称)
// 方法实现
@end
二、 类的本质
typedef struct objc_class *Class;
Class c = [Person class]; // 类方法
或者
Person *p = [Person new];
Class c2 = [p class]; // 对象方法
Class c = [Person class];
Person *p2 = [c new];
使用NSLog和%@输出某个对象时,会调用对象的-description方法,并拿到返回值进行输出
使用NSLog和%@输出某个类对象时,会调用类的+description方法,并拿到返回值进行输出
区别
+description方法决定了类对象的输出结果,即类本身
-description方法决定了实例对象的输出结果,即Person创建的对象。
1 // 2 // main.m 3 // 07-description方法 4 // 5 // Created by apple on 13-8-8. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Person.h" 11 12 13 void test9() 14 { 15 // 输出当前函数名 16 NSLog(@"%s\n", __func__); 17 } 18 19 int main() 20 { 21 // 输出行号 22 NSLog(@"%d", __LINE__); 23 24 // NSLog输出C语言字符串的时候,不能有中文 25 // NSLog(@"%s", __FILE__); 26 27 // 输出源文件的名称 28 printf("%s\n", __FILE__); 29 30 test9(); 31 32 Person *p = [[Person alloc] init]; 33 34 // 指针变量的地址 35 NSLog(@"%p", &p); 36 // 对象的地址 37 NSLog(@"%p", p); 38 // <类名:对象地址> 39 NSLog(@"%@", p); 40 41 return 0; 42 } 43 44 void test2() 45 { 46 Class c = [Person class]; 47 48 // 1.会调用类的+description方法 49 // 2.拿到+description方法的返回值(NSString *)显示到屏幕上 50 NSLog(@"%@", c); 51 } 52 53 void test1() 54 { 55 Person *p = [[Person alloc] init]; 56 p.age = 20; 57 p.name = @"Jack"; 58 // 默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址> 59 60 // 1.会调用对象p的-description方法 61 // 2.拿到-description方法的返回值(NSString *)显示到屏幕上 62 // 3.-description方法默认返回的是“类名+内存地址” 63 NSLog(@"%@", p); 64 65 //Person *p2 = [[Person alloc] init]; 66 //NSLog(@"%@", p2); 67 68 //NSString *name = @"Rose"; 69 70 //NSLog(@"我的名字是%@", name); 71 72 Person *p2 = [[Person alloc] init]; 73 p2.age = 25; 74 p2.name = @"Jake"; 75 76 NSLog(@"%@", p2); 77 }
typedef struct objc_selector *SEL;
SEL s = @selector(test);
SEL s2 = NSSelectorFromString(@"test");
// 将SEL对象转为NSString对象
NSString *str = NSStringFromSelector(@selector(test));
Person *p = [Person new];
// 调用对象p的test方法
[p performSelector:@selector(test)];
// 下面的代码会引发死循环
- (void)test {
[self performSelector:_cmd];
}
六、点语法
(一)认识点语法
声明一个Person类:
1 // 2 // Person.h 3 // 04-点语法 4 // 5 // Created by apple on 13-8-7. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface Person : NSObject 12 { 13 int _age; 14 NSString *_name; 15 } 16 17 - (void)setAge:(int)age; 18 - (int)age; 19 20 21 - (void)setName:(NSString *)name; 22 - (NSString *)name; 23 24 @end
Person类的实现:
1 // 2 // Person.m 3 // 04-点语法 4 // 5 // Created by apple on 13-8-7. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import "Person.h" 10 11 @implementation Person 12 13 - (void)setAge:(int)age 14 { 15 //_age = age; 16 17 NSLog(@"setAge:"); 18 19 // 会引发死循环 20 //self.age = age; // [self setAge:age]; 21 } 22 23 - (int)age 24 { 25 NSLog(@"age"); 26 return _age; 27 // 会引发死循环 28 //return self.age;// [self age]; 29 } 30 31 - (void)setName:(NSString *)name 32 { 33 _name = name; 34 } 35 36 - (NSString *)name 37 { 38 return _name; 39 } 40 41 @end
点语法的使用:
1 // 2 // main.m 3 // 04-点语法 4 // 5 // Created by apple on 13-8-7. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Person.h" 11 12 int main(int argc, const char * argv[]) 13 { 14 Person *p = [Person new]; 15 16 // 点语法的本质还是方法调用 17 p.age = 10; // [p setAge:10]; 18 19 int a = p.age; // [p age]; 20 21 22 p.name = @"Jack"; 23 24 NSString *s = p.name; 25 26 NSLog(@"%@", s); 27 28 return 0; 29 }
二)点语法的作用
OC设计点语法的目的,是为了让其他语言的开发者可以很快的上手OC语言开发,使用点语法,让它和其他面向对象的语言如java很像。
(三)点语法的本质
点语法的本质是方法的调用,而不是访问成员变量,当使用点语法时,编译器会自动展开成相应的方法。切记点语法的本质是转换成相应的set和get方法,如果没有set和get方法,则不能使用点语法。
如:
Stu.age=10;展开为:[stu setAge:10];
int a=stu.age;展开为:[stu age];
编译器如何知道是set方法还是get方法?主要是看赋值(可以使用断点调试来查看)。
在OC中访问成员变量只有一种方式即使用-> 如stu->age,这种情况要求在@public的前提下。
(四)点语法的使用注意
下面的使用方式是一个死循环:
(1)在set方法中,self.age=age;相当于是[self setAge:age];
(2)在get方法中,return self.age;相当于是[self age];
1 点语法(找出不合理的地方) 2 #import <Foundation/Foundation.h> 3 @interface Person : NSObject 4 { 5 int _age; 6 } 7 - (void)setAge:(int)age; 8 - (int)age; 9 @end 10 11 @implementation Person 12 { 13 int _age; 14 } 15 - (void)setAge:(int)age 16 { 17 _age = age; 18 // 会引发死循环 19 // self.age = age; 20 } 21 - (int)age 22 { 23 return _age; 24 // 会引发死循环 25 // return self.age; 26 } 27 @end 28 29 // 2个不合理
七、变量作用域
(一)变量的作用域主要分为四种:
(1)@public (公开的)在有对象的前提下,任何地方都可以直接访问。
(2)@protected (受保护的)只能在当前类和子类的对象方法中访问
(3)@private (私有的)只能在当前类的对象方法中才能直接访问
(4)@package (框架级别的)作用域介于私有和公开之间,只要处于同一个框架中就可以直接通过变量名访问
1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 { 5 int _no; 6 7 @public // 在任何地方都能直接访问对象的成员变量 8 int _age; 9 10 11 @private // 只能在当前类的对象方法中直接访问 12 int _height; 13 14 @protected // 能在当前类和子类的对象方法中直接访问 15 int _weight; 16 int _money; 17 } 18 19 - (void)setHeight:(int)height; 20 - (int)height; 21 22 - (void)test; 23 @end
(二)使用注意和补充
(1)在类的实现即.m文件中也可以声明成员变量,但是因为在其他文件中通常都只是包含头文件而不会包含实现文件,所以在这里声明的成员变量是@private的。在.m中定义的成员变量不能喝它的头文件.h中的成员变量同名,在这期间使用@public等关键字也是徒劳的。
(2)在@interface @end之间声明的成员变量如果不做特别的说明,那么其默认是protected的。
(3)一个类继承了另一个类,那么就拥有了父类的所有成员变量和方法,注意所有的成员变量它都拥有,只是有的它不能直接访问。
八、@property @synthesize关键字
注意:这两个关键字是编译器特性,让xcode可以自动生成getter和setter的声明和实现。
(一)@property 关键字
@property 关键字可以自动生成某个成员变量的setter和getter方法的声明
@property int age;
编译时遇到这一行,则自动扩展成下面两句:
- (void)setAge:(int)age;
- (int)age;
(二)@synthesize关键字
@synthesize关键字帮助生成成员变量的setter和getter方法的实现。
语法:@synthesize age=_age;
相当于下面的代码:
- (void)setAge:(int)age
{
_age=age;
}
- (int)age
{
Return _age;
}
(三)关键字的使用和使用注意
类的声明部分:
类的实现部分:
测试程序:
新版本中:
类的声明部分:
类的实现部分:
测试程序:
(1)在老式的代码中,@property只能写在@interface @end中,@synthesize只能写在@implementation @end中,自从xcode 4.4后,@property就独揽了@property和@synthesize的功能。
(2)@property int age;这句话完成了3个功能:1)生成_age成员变量的get和set方法的声明;2)生成_age成员变量set和get方法的实现;3)生成一个_age的成员变量。
注意:这种方式生成的成员变量是private的。
(3)可以通过在{}中加上int _age;显示的声明_age为protected的。
(4)原则:get和set方法同变量一样,如果你自己定义了,那么就使用你已经定义的,如果没有定义,那么就自动生成一个。
(5)手动实现:
1)如果手动实现了set方法,那么编译器就只生成get方法和成员变量;
2)如果手动实现了get方法,那么编译器就只生成set方法和成员变量;
3)如果set和get方法都是手动实现的,那么编译器将不会生成成员变量。
九、Id
id 是一种类型,万能指针,能够指向\操作任何的对象。
注意:在id的定义中,已经包好了*号。Id指针只能指向os的对象。
id 类型的定义
Typedef struct objc object{
Class isa;
} *id;
局限性:调用一个不存在的方法,编译器会马上报错。
1 // 2 // main.m 3 // 01-id 4 // 5 // Created by apple on 13-8-8. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Person.h" 11 12 13 void test(id d) 14 { 15 16 } 17 18 int main(int argc, const char * argv[]) 19 { 20 21 @autoreleasepool { 22 Person *p = [Person new]; 23 //[p fsdfdsfd]; 24 25 NSObject *o = [Person new]; 26 27 28 // id == NSObject * 29 // 万能指针,能指向\操作任何OC对象 30 id d = [Person new]; 31 32 [d setAge:10]; 33 34 [d setObj:@"321423432"]; 35 36 NSLog(@"%d", [d age]); 37 } 38 return 0; 39 }
十、OC语言构造方法
一、构造方法
构造方法的调用
完整的创建一个可用的对象:Person *p=[Person new];
New方法的内部会分别调用两个方法来完成2件事情,1)使用alloc方法来分配存储空间(返回分配的对象);2)使用init方法来对对象进行初始化。
可以把new方法拆开如下:
1.调用类方法+alloc分配存储空间,返回未经初始化的对象
Person *p1=[person alloc];
2.调用对象方法-init进行初始化,返回对象本身
Person *p2=[p1 init];
3.以上两个过程整合为一句:
Person *p=[[Person alloc] init];
说明:init方法就是构造方法,是用来初始化对象的方法,注意这是一个对象方法,一减号开头。默认初始化完毕后,所有成员变量的值都为0。
构造方法使用注意
(1)子类拥有的成员变量包括自己的成员变量以及从父类继承而来的成员变量,在重写构造方法的时候应该首先对从父类继承而来的成员变量先进行初始化。
(2)原则:先初始化父类的,再初始化子类的。
(3)重写构造方法的目的:为了让对象方法一创建出来,成员变量就会有一些固定的值。
(4)注意点:#1先调用父类的构造方法[super init]; #2再进行子类内部成员变量的初始化。
二、自定义构造方法
(一)自定义构造方法的规范
(1)一定是对象方法,以减号开头
(2)返回值一般是id类型
(3)方法名一般以initWith开头
(二)自定义构造方法的代码实现
Person类的声明,其中声明了两个接收参数的自定义构造方法
1 // 2 // Person.h 3 // 03-自定义构造方法 4 // 5 // Created by apple on 13-8-8. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface Person : NSObject 12 @property NSString *name; 13 @property int age; 14 15 /* 16 自定义构造方法的规范 17 1.一定是对象方法,一定以 - 开头 18 2.返回值一般是id类型 19 3.方法名一般以initWith开头 20 */ 21 22 - (id)initWithName:(NSString *)name; 23 24 - (id)initWithAge:(int)age; 25 26 // initWithName:andAge: 27 - (id)initWithName:(NSString *)name andAge:(int)age; 28 29 @end
Person类的实现
1 // 2 // Person.m 3 // 03-自定义构造方法 4 // 5 // Created by apple on 13-8-8. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import "Person.h" 10 11 @implementation Person 12 13 - (id)init 14 { 15 if ( self = [super init] ) 16 { 17 _name = @"Jack"; 18 } 19 return self; 20 } 21 22 - (id)initWithName:(NSString *)name 23 { 24 25 if ( self = [super init] ) 26 { 27 _name = name; 28 } 29 30 return self; 31 } 32 33 - (id)initWithAge:(int)age 34 { 35 if ( self = [super init] ) 36 { 37 _age = age; 38 } 39 return self; 40 } 41 42 - (id)initWithName:(NSString *)name andAge:(int)age 43 { 44 if ( self = [super init] ) 45 { 46 _name = name; 47 _age = age; 48 } 49 return self; 50 } 51 52 @end
Student继承自Person类,声明了一个接收三个参数的构造方法
1 // 2 // Student.h 3 // 03-自定义构造方法 4 // 5 // Created by apple on 13-8-8. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import "Person.h" 10 11 @interface Student : Person 12 @property int no; 13 14 - (id)initWithNo:(int)no; 15 16 - (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no; 17 18 @end
Student类的实现
1 // 2 // Student.m 3 // 03-自定义构造方法 4 // 5 // Created by apple on 13-8-8. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import "Student.h" 10 11 @implementation Student 12 - (id)initWithNo:(int)no 13 { 14 if ( self = [super init] ) 15 { 16 _no = no; 17 } 18 return self; 19 } 20 21 // 父类的属性交给父类方法去处理,子类方法处理子类自己的属性 22 - (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no 23 { 24 // 将name、age传递到父类方法中进行初始化 25 if ( self = [super initWithName:name andAge:age]) 26 { 27 _no = no; 28 } 29 30 return self; 31 } 32 33 //- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no 34 //{ 35 // if ( self = [super init] ) 36 // { 37 // _no = no; 38 // //_name = name; 39 // self.name = name; 40 // self.age = age; 41 // 42 // //[self setName:name]; 43 // //[self setAge:age]; 44 // } 45 // 46 // return self; 47 //} 48 @end
测试主程序
1 // 2 // main.m 3 // 03-自定义构造方法 4 // 5 // Created by apple on 13-8-8. 6 // Copyright (c) 2013年 itcast. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Person.h" 11 #import "Student.h" 12 13 int main(int argc, const char * argv[]) 14 { 15 16 @autoreleasepool { 17 Student *p = [[Student alloc] initWithName:@"Jim" andAge:29 andNo:10]; 18 NSLog(@"00000"); 19 } 20 return 0; 21 }
(三)自定义构造方法的使用注意
(1)自己做自己的事情
(2)父类的方法交给父类的方法来处理,子类的方法处理子类自己独有的属性