1、 分类-Category
1. 基本用途:Category 分类是OC特有的语言,依赖于类。
? 如何在不改变原来类模型的前提下,给类扩充一些方法?有2种方式
● 继承
● 分类(Category)
2. 格式
? 分类的声明
@interface 类名 (分类名称)
// 方法声明
@end
? 分类的实现
@implementation 类名 (分类名称)
// 方法实现
@end
3. 好处
? 一个庞大的类可以分模块开发
? 一个庞大的类可以由多个人来编写,更有利于团队合作
?
4. 给系统自带的类添加分类
? 给NSString增加一个类方法:计算某个字符串中阿拉伯数字的个数
? 给NSString增加一个对象方法:计算当前字符串中阿拉伯数字的个数
5. 注意
? Category可以访问原始类的实例变量,但不能添加变量,只能添加方法。如果想添加变量,可以考虑通过继承创建子类
? Category可以实现原始类的方法,但不推荐这么做,因为它是直接替换掉原来的方法,这么做的后果是再也不能访问原来的方法
? 多个Category中如果实现了相同的方法,只有最后一个参与编译的才会有效
2、 类的本质
1. 类也是个对象
? 其实类也是一个对象,是Class类型的对象,简称“类对象”
? Class类型的定义
typedef struct objc_class *Class;
? 类名就代表着类对象,每个类只有一个类对象
2. +load和+initialize
? +load
● 在程序启动的时候会加载所有的类和分类,并调用所有类和分类的+load方法
● 先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load
● 先加载元原始类,再加载分类
● 不管程序运行过程有没有用到这个类,都会调用+load加载
? +initialize
● 在第一次使用某个类时(比如创建对象等),就会调用一次+initialize方法
● 一个类只会调用一次+initialize方法,先调用父类的,再调用子类的
3. 获取类对象的2种方式
Class c = [Person class]; // 类方法
或者
Person *p = [Person new];
Class c2 = [p class]; // 对象方法
4. 类对象调用类方法
Class c = [Person class];
Person *p2 = [c new];
3、 description方法
1. -description对象方法
使用NSLog和%@输出某个对象时,会调用对象的-description方法,并拿到返回值进行输出
2. + description类方法
使用NSLog和%@输出某个类对象时,会调用类的+description方法,并拿到返回值进行输出
3. 修改NSLog的默认输出
● 重写-description或者+description方法即可
4. 死循环陷阱
● 如果在-description方法中使用NSLog打印self
区别
+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 }
4、 SEL
1. 方法的存储位置
? 每个类的方法列表都存储在类对象中
? 每个方法都有一个与之对应的SEL类型的对象
? 根据一个SEL对象就可以找到方法的地址,进而调用方法
? SEL类型的定义
typedef struct objc_selector ?*SEL;
2. SEL对象的创建
SEL s = @selector(test);
SEL s2 = NSSelectorFromString(@"test");
3. SEL对象的其他用法
// 将SEL对象转为NSString对象
NSString *str = NSStringFromSelector(@selector(test));
Person *p = [Person new];
// 调用对象p的test方法
[p performSelector:@selector(test)];
5、 NSLog输出增强
? __FILE__ :源代码文件名
? __LINE__ :NSLog代码在第几行
? _cmd :代表着当前方法的SEL
// 下面的代码会引发死循环
- (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)父类的方法交给父类的方法来处理,子类的方法处理子类自己独有的属性