黑马程序员---OC基础知识⑥
2015-04-08 18:21
232 查看
1.id类型
id在OC中可以理解为是一个万能指针,它能够相当于任何数据类型的指针变量
在实际开发中就可以把id当做能操作任何oc对象的万能指针类来使用了。如:
#import <Foundation/Foundation.h>
#import "Person.h"
int main() {
Person *p = [Person new];
//id== NSObject *可以这样认为
// 万能指针,能操作任何oc对象
id d = [Person new];
[d setAge:10];
NSLog(@"%d", [d age]);
return 0;
}
2.构造方法
在之前的学习中,创建一个类的方式是调用new这个类方法,如
[Person new];
其创建之后会返回一个Person类的实例,也就是一个Person对象,通常我们再使用一个Person类的指针接收它,以便之后的各种操作,如:
Person *p = [Person new];
new方法的实质是这样的:
new方法内部执行的过程
完整地创建一个可用的对象
1.分配存储空间 +alloc
2.初始化 -init
new方法实际上调用了+alloc和-init两个方法返回类的对象。因此上面的代码
Person *p = [Person new];
实际上就相当于
Person *p = [[Person alloc] init];
分配空间+alloc这一步骤在相同类型的对象的创建过程是相同的,而初始化-init是不同的,因为不同的对象的属性值可能是不同的,现在我们就是通过使用不同的-init函数来进行对不同对象的构造,因而以后创建对象时要坚持使用这种写法
Person *p = [[Person alloc] init];
重写构造方法:
构造方法-init是在NSObject中定义的,若要重写它,只需在类的实现部分重写-init即可:
// 重写init方法
- (id)init
{
/*
// 1.一定要调用super的init方法:初始化父类中声明的一些成员变量和其他属性
self = [super init]; // 返回当前对象
// 2.如果对象初始化成功,才有必要进行接下来的初始化
if(self)// if(self != nil)
{// 初始化成功
_age = 10;
}
*/
if( self = [super init])// 写在一条语句中
{
_age = 10;
}
// 3.返回一个已经初始化完毕的对象
return self;
}
重写-init函数必须按照上面三步走的步骤方法,这样才能创建出可用的合理的对象。
有时候希望在初始化时使用自己传入的值进行初始化对象的属性,这时候就用到了带参数的自定义构造方法。
例如为Person类添加可以为name属性赋初值的构造方法
- (instancetype)initWithName:(NSString *)name
{
if(self = [super init])
{
_name = name;
}
return self;
}
Person *p = [[Person alloc] initWithName:@"Rose"];
自定义构造方法的定义有如下规范:
自定义构造方法
1.一定是对象方法,一定以-开头
2.返回值一定是id类型
3.方法名一般以initWith开头
与上面类似的,可以创建多个参数的构造方法:
- (instancetype)initWithName:(NSString *)name andAge:(int)age
{
if ( self = [super init] )
{
_name = name;
_age = age;
}
return self;
}
当类在创建时,有时候会对父类中继承过来的成员变量也进行初始化,这时候就有如下的解决方案:
Student类中有自己的成员变量no而name和age成员变量继承自父类Person,用Student的带参数构造方法直接为name、age和no三个成员变量赋值
- (instancetype)initWithName:(NSString *)name andAge:(int)age andNo:(int)no
{
// if ( self = [super init] )
// {
// self.name = name;
// self.age = age;
// _no = no;
// }
if( self = [super initWithName:name andAge:age] )
{
_no = no;
}
return self;
}
如代码所示,推荐使用父类构造方法对继承自父类的成员变量进行初始化,然后对自己特有的成员变量进行初始化。
3.更改Xcode的模板
①添加或者更改可以创建的项目类型和描述信息
转到路径/Applications/Xcode_5.1.1.app/Contents/Developer/Library/Xcode/Templates/Project Templates
如果是修改mac项目的模板就进入Mac文件夹,这时候文件夹显示的内容和创建Mac项目时的选项是一致的
进入要修改的项目的文件夹,打开TemplateInfo.plist文件查找对应的条目修改,如描述对应的时Description条目,修改其对应的value即可。
②修改项目创建时文件内容的模板
依然按照上面的步骤打开TemplateInfo.plist文件,然后找到Definition条目,修改对应的值。
③修改文件模板中的注释(注意与上面是不同的)
转到的路径是文件模板而不是项目模板
如果是修改OC类文件的注释进入这个目录下面:/Applications/Xcode_5.1.1.app/Contents/Developer/Library/Xcode/Templates/File Templates/Cocoa/Objective-C category.xctemplate/NSObject/
这里面有两个文件分别是___FILEBASENAME___.h和___FILEBASENAME___.m对应创建类时生成的两个文件,
注释模板里面有对许多值的宏定义,会在生成文件时替换。Apple文档里面给出了这些值的定义:
4.分类
1. 基本用途
如何在不改变原来类模型的前提下,给类扩充一些方法?有2种方式
继承
分类(Category)
2. 格式
分类的声明
@interface 类名 (分类名称)
// 方法声明
@end
@implementation 类名 (分类名称)
// 方法实现
@end
3.好处
一个庞大的类可以分模块开发
一个庞大的类可以由多个人来编写,更有利于团队合作
使用注意:
1.分类只能增加方法 不能增加成员变量
2.分类方法实现中可以访问原来类中声明的成员变量
3.分类可以重新实现原来类中的方法,但会覆盖原来的方法,会导致原来类方法不能使用(不建议)
4.方法调用优先级:同名方法,优先去分类中查找(最后参与编译的分类),然后去原来类中找,最后再去父类中找
使用示例:
给NSString增加一个类方法:计算某个字符串中阿拉伯数字的个数
首先在项目中创建一个分类
Category输入为Number
Category on 输入NSString
在NSString+Number.h文件中:
@interface NSString (Number)
- (int)numberCount;
@end
在NSString+Number.m文件中:
#import "NSString+Number.h"
@implementation NSString (Number)
- (int)numberCount
{
int count = 0;
for (int i = 0; i<[self length]; i++)
{
unichar c = [self characterAtIndex:i];
if (c >= '0' && c <= '9')//注意此处要单引号
count++;
}
return count;
}
@end
5.对类的深入研究---类的本质
类本身也是一个对象,是Class类型的对象,简称类对象
Person *p1 = [[Person alloc] init];
Class c = [p1 class]; // 对象有-class方法
Class c1 = [Person class];// 此时c ==c1 类有+class方法
[c test];// 类对象==类 调用了+test() 其实相当于[Person test];
Perosn *p2 = [[c alloc] new];// 用类对象创建该类型的对象
6.对类的深入研究---类的加载过程
类的加载过程
+load在类被加载的时候调用
@interface Person
//当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法
+ (void)load
{
NSLog(@"Perosn---load");
}
//当第一次使用这个类的时候,就会调用一次initialize方法
+ (void)initialize
{
NSLog(@"Person---initialize");
}
上面的两种方法都是先加载父类同名方法
项目启动时 会加载所有的类和分类load
使用类时 若有分类只调用分类的initialize
总结:
1.当程序启动时,会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只会调用一次
2.当第一次使用某个类时,就会调用当前类的+initialize方法
3.先加载父类,再加载子类(先调用父类的+load方法在调用子类的load方法)
先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)
7.-description方法
1.NSLog输出对象时,首先会调用对象的description方法
2.拿到-description方法的返回值(NSString *)显示到屏幕上输出
3.-description方法会默认返回的是“类名+内存地址”
要想输出对象时输出一些信息,就要重写-description方法
- (NSString *)description
{
// 下面代码会引发死循环
// NSLog(@"%@", self);
eturn [NSString stringWithFormat:@"age=%d, name=%@", _age, _name];
}
8.对NSLog()函数的使用补充
NSLog 输出字符串的时候不能有中文,但是printf()可以
//下面是输出一些系统定义的宏
printf("%s\n", __FILE__);
NSLog(@"%d", __LINE__);
NSLog(@"%s", __func__);
NSLog(@"%@", &p); // 输出对象的地址(在没有重写-description的情况下)
NSLog(@"%p", p); // 输出指针变量的地址
9.SEL的使用
SEL知识点类似于Java的反射Method
SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址,找到方法地址就可以调用方法
Person *p = [[Person alloc] init];
[p test2];
上面的语句执行时,会进行下面的操作:
// 1.首先会把test2包装成SEL类型的数据
// 2.根据SEL数据找到相应的方法地址
// 3.根据方法地址调用对应的方法
间接调用test2()方法
[p performSelector:@selector(test2)];// 间接调用test2方法
带参数的方法动态调用
[p test3:@"123"]; // 直接调用
[p performSelector:@selector(test3:) withObject:@"123"]; // 间接调用
SEL的创建方法
SEL s = @selector(test3:);// 方法一 直接传入方法名
SEL s = NSSelectorFromString(@"test3:")// 方法二 传入字符串
每个方法内部的SEL类型变量_cmd代表当前方法 Current Method但是_cmd只能在方法的内部使用,而不能在函数的内部使用,下面的例子用于打印输出当前执行的函数:
在Perosn类中添加方法:
- (void)printMethodName{
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"%@", str);
}
在main函数中:
Person *p = [[Person alloc] init];
[p printMethodcName];
id在OC中可以理解为是一个万能指针,它能够相当于任何数据类型的指针变量
在实际开发中就可以把id当做能操作任何oc对象的万能指针类来使用了。如:
#import <Foundation/Foundation.h>
#import "Person.h"
int main() {
Person *p = [Person new];
//id== NSObject *可以这样认为
// 万能指针,能操作任何oc对象
id d = [Person new];
[d setAge:10];
NSLog(@"%d", [d age]);
return 0;
}
2.构造方法
在之前的学习中,创建一个类的方式是调用new这个类方法,如
[Person new];
其创建之后会返回一个Person类的实例,也就是一个Person对象,通常我们再使用一个Person类的指针接收它,以便之后的各种操作,如:
Person *p = [Person new];
new方法的实质是这样的:
new方法内部执行的过程
完整地创建一个可用的对象
1.分配存储空间 +alloc
2.初始化 -init
new方法实际上调用了+alloc和-init两个方法返回类的对象。因此上面的代码
Person *p = [Person new];
实际上就相当于
Person *p = [[Person alloc] init];
分配空间+alloc这一步骤在相同类型的对象的创建过程是相同的,而初始化-init是不同的,因为不同的对象的属性值可能是不同的,现在我们就是通过使用不同的-init函数来进行对不同对象的构造,因而以后创建对象时要坚持使用这种写法
Person *p = [[Person alloc] init];
重写构造方法:
构造方法-init是在NSObject中定义的,若要重写它,只需在类的实现部分重写-init即可:
// 重写init方法
- (id)init
{
/*
// 1.一定要调用super的init方法:初始化父类中声明的一些成员变量和其他属性
self = [super init]; // 返回当前对象
// 2.如果对象初始化成功,才有必要进行接下来的初始化
if(self)// if(self != nil)
{// 初始化成功
_age = 10;
}
*/
if( self = [super init])// 写在一条语句中
{
_age = 10;
}
// 3.返回一个已经初始化完毕的对象
return self;
}
重写-init函数必须按照上面三步走的步骤方法,这样才能创建出可用的合理的对象。
有时候希望在初始化时使用自己传入的值进行初始化对象的属性,这时候就用到了带参数的自定义构造方法。
例如为Person类添加可以为name属性赋初值的构造方法
- (instancetype)initWithName:(NSString *)name
{
if(self = [super init])
{
_name = name;
}
return self;
}
Person *p = [[Person alloc] initWithName:@"Rose"];
自定义构造方法的定义有如下规范:
自定义构造方法
1.一定是对象方法,一定以-开头
2.返回值一定是id类型
3.方法名一般以initWith开头
与上面类似的,可以创建多个参数的构造方法:
- (instancetype)initWithName:(NSString *)name andAge:(int)age
{
if ( self = [super init] )
{
_name = name;
_age = age;
}
return self;
}
当类在创建时,有时候会对父类中继承过来的成员变量也进行初始化,这时候就有如下的解决方案:
Student类中有自己的成员变量no而name和age成员变量继承自父类Person,用Student的带参数构造方法直接为name、age和no三个成员变量赋值
- (instancetype)initWithName:(NSString *)name andAge:(int)age andNo:(int)no
{
// if ( self = [super init] )
// {
// self.name = name;
// self.age = age;
// _no = no;
// }
if( self = [super initWithName:name andAge:age] )
{
_no = no;
}
return self;
}
如代码所示,推荐使用父类构造方法对继承自父类的成员变量进行初始化,然后对自己特有的成员变量进行初始化。
3.更改Xcode的模板
①添加或者更改可以创建的项目类型和描述信息
转到路径/Applications/Xcode_5.1.1.app/Contents/Developer/Library/Xcode/Templates/Project Templates
如果是修改mac项目的模板就进入Mac文件夹,这时候文件夹显示的内容和创建Mac项目时的选项是一致的
进入要修改的项目的文件夹,打开TemplateInfo.plist文件查找对应的条目修改,如描述对应的时Description条目,修改其对应的value即可。
②修改项目创建时文件内容的模板
依然按照上面的步骤打开TemplateInfo.plist文件,然后找到Definition条目,修改对应的值。
③修改文件模板中的注释(注意与上面是不同的)
转到的路径是文件模板而不是项目模板
如果是修改OC类文件的注释进入这个目录下面:/Applications/Xcode_5.1.1.app/Contents/Developer/Library/Xcode/Templates/File Templates/Cocoa/Objective-C category.xctemplate/NSObject/
这里面有两个文件分别是___FILEBASENAME___.h和___FILEBASENAME___.m对应创建类时生成的两个文件,
注释模板里面有对许多值的宏定义,会在生成文件时替换。Apple文档里面给出了这些值的定义:
4.分类
1. 基本用途
如何在不改变原来类模型的前提下,给类扩充一些方法?有2种方式
继承
分类(Category)
2. 格式
分类的声明
@interface 类名 (分类名称)
// 方法声明
@end
@implementation 类名 (分类名称)
// 方法实现
@end
3.好处
一个庞大的类可以分模块开发
一个庞大的类可以由多个人来编写,更有利于团队合作
使用注意:
1.分类只能增加方法 不能增加成员变量
2.分类方法实现中可以访问原来类中声明的成员变量
3.分类可以重新实现原来类中的方法,但会覆盖原来的方法,会导致原来类方法不能使用(不建议)
4.方法调用优先级:同名方法,优先去分类中查找(最后参与编译的分类),然后去原来类中找,最后再去父类中找
使用示例:
给NSString增加一个类方法:计算某个字符串中阿拉伯数字的个数
首先在项目中创建一个分类
Category输入为Number
Category on 输入NSString
在NSString+Number.h文件中:
@interface NSString (Number)
- (int)numberCount;
@end
在NSString+Number.m文件中:
#import "NSString+Number.h"
@implementation NSString (Number)
- (int)numberCount
{
int count = 0;
for (int i = 0; i<[self length]; i++)
{
unichar c = [self characterAtIndex:i];
if (c >= '0' && c <= '9')//注意此处要单引号
count++;
}
return count;
}
@end
5.对类的深入研究---类的本质
类本身也是一个对象,是Class类型的对象,简称类对象
Person *p1 = [[Person alloc] init];
Class c = [p1 class]; // 对象有-class方法
Class c1 = [Person class];// 此时c ==c1 类有+class方法
[c test];// 类对象==类 调用了+test() 其实相当于[Person test];
Perosn *p2 = [[c alloc] new];// 用类对象创建该类型的对象
6.对类的深入研究---类的加载过程
类的加载过程
+load在类被加载的时候调用
@interface Person
//当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法
+ (void)load
{
NSLog(@"Perosn---load");
}
//当第一次使用这个类的时候,就会调用一次initialize方法
+ (void)initialize
{
NSLog(@"Person---initialize");
}
上面的两种方法都是先加载父类同名方法
项目启动时 会加载所有的类和分类load
使用类时 若有分类只调用分类的initialize
总结:
1.当程序启动时,会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只会调用一次
2.当第一次使用某个类时,就会调用当前类的+initialize方法
3.先加载父类,再加载子类(先调用父类的+load方法在调用子类的load方法)
先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)
7.-description方法
1.NSLog输出对象时,首先会调用对象的description方法
2.拿到-description方法的返回值(NSString *)显示到屏幕上输出
3.-description方法会默认返回的是“类名+内存地址”
要想输出对象时输出一些信息,就要重写-description方法
- (NSString *)description
{
// 下面代码会引发死循环
// NSLog(@"%@", self);
eturn [NSString stringWithFormat:@"age=%d, name=%@", _age, _name];
}
8.对NSLog()函数的使用补充
NSLog 输出字符串的时候不能有中文,但是printf()可以
//下面是输出一些系统定义的宏
printf("%s\n", __FILE__);
NSLog(@"%d", __LINE__);
NSLog(@"%s", __func__);
NSLog(@"%@", &p); // 输出对象的地址(在没有重写-description的情况下)
NSLog(@"%p", p); // 输出指针变量的地址
9.SEL的使用
SEL知识点类似于Java的反射Method
SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址,找到方法地址就可以调用方法
Person *p = [[Person alloc] init];
[p test2];
上面的语句执行时,会进行下面的操作:
// 1.首先会把test2包装成SEL类型的数据
// 2.根据SEL数据找到相应的方法地址
// 3.根据方法地址调用对应的方法
间接调用test2()方法
[p performSelector:@selector(test2)];// 间接调用test2方法
带参数的方法动态调用
[p test3:@"123"]; // 直接调用
[p performSelector:@selector(test3:) withObject:@"123"]; // 间接调用
SEL的创建方法
SEL s = @selector(test3:);// 方法一 直接传入方法名
SEL s = NSSelectorFromString(@"test3:")// 方法二 传入字符串
每个方法内部的SEL类型变量_cmd代表当前方法 Current Method但是_cmd只能在方法的内部使用,而不能在函数的内部使用,下面的例子用于打印输出当前执行的函数:
在Perosn类中添加方法:
- (void)printMethodName{
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"%@", str);
}
在main函数中:
Person *p = [[Person alloc] init];
[p printMethodcName];
相关文章推荐
- 黑马程序员——OC语言基础知识
- 黑马程序员---OC基础知识②
- 【黑马程序员】-OC基础知识总结
- 黑马程序员——OC基础学习(四)---封装,继承和多态的学习知识总结
- 黑马程序员——OC学习笔记01 OC基础知识。
- 黑马程序员---OC基础知识③
- 黑马程序员--IOS入学学习--6-OC基础知识
- 黑马程序员---OC基础知识⑦
- 黑马程序员—OC学习日记.OC基础知识
- 黑马程序员---OC基础知识⑧
- 黑马程序员——OC基础知识
- 黑马程序员—OC语言基础—基础语法知识
- 黑马程序员---OC基础知识④
- 【黑马程序员-学习笔记】OC-基础知识总结
- 黑马程序员 ——OC基础知识
- 黑马程序员---OC基础知识⑤
- 黑马程序员---OC基础知识①
- 黑马程序员-③oc基础知识
- 黑马程序员__oc基础知识总结
- 黑马程序员——OC基础学习(二)---对象方法和类方法的学习知识总结