------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、构造方法
在之前的学习的[Person new]可以创建一个类 并且能够初始化对象,实际在开发中new不常用,通常情况下用构造的方法来对成员变量初始化。
1. 完整的创建一个可用的对象分为两步
第一步:分配存储空间 调用的是+alloc方法
第二步:.初始化 -init
[person new]拆分后是:
Person *p1 = [Person alloc];
Person *p1 = [p1 init];
合成一句后:
Person *p1= [[Person alloc] init];
2.init方法的重写过程:
有时候new给出的初始值并不是所需的值,因此要用到构造方法,用来初始化对象的方法,因此要重写-(id)init,它是NSObject类中的对象方法在NSObject类中的init初始化过程:
1.一定要调用super类的init:初始化父类中声明的成员变量和其他属性 self=[super init] 然后返回一个对象给self 当前对象 初始化父类声明中的成员变量
2.'如果初始化成功,才对当前的对象进行自己所规定的变量进行初始化
3.返回一个已经初始化话完毕的对象
在类中重写init也要遵循NSObject中的过程:
- (id)init
{
if (self = [super init])
{
_age = 10;
}
return self;
}
注意:
init 初始化的顺序是:NSObject 父类 子类
3.自定义的构造方法
有时想要随时修改初始值,那么就不能重写构造方法,而是要自定义构造方法,这样可以使外部能够灵活改变初始化的值。原则:对于自己的属性就由自己处理,父类有的
属性就交给父类处理,可以避免父类中的东西修改对子类无影响。
注意:
1.一定是对象方法,一定以 - 开头
2.返回值一般是id类型
3.方法名一般以initWith开头
- (id)initWithAge:(int)age{
if (self = [super init] {
_age = age;
}
return self;
}
person.h#import <Foundation/Foundation.h>
@interface Person : NSObject
@property NSString *name;
@property int age;
- (id)initWithName:(NSString *)name;
- (id)initWithAge:(int)age;
// initWithName:andAge:
- (id)initWithName:(NSString *)name andAge:(int)age;
@end
person.m
#import "Person.h"
@implementation Person
- (id)init
{
if ( self = [super init] )
{
_name = @"Jack";
}
return self;
}
- (id)initWithName:(NSString *)name
{
if ( self = [super init] )
{
_name = name;
}
return self;
}
- (id)initWithAge:(int)age
{
if ( self = [super init] )
{
_age = age;
}
return self;
}
- (id)initWithName:(NSString *)name andAge:(int)age
{
if ( self = [super init] )
{
_name = name;
_age = age;
}
return self;
}
@end
student.h
#import "Person.h"
@interface Student : Person
@property int no;
- (id)initWithNo:(int)no;
- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no;
@end
student.m
#import "Student.h"
@implementation Student
- (id)initWithNo:(int)no
{
if ( self = [super init] )
{
_no = no;
}
return self;
}
// 父类的属性交给父类方法去处理,子类方法处理子类自己的属性
- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no
{
// 将name、age传递到父类方法中进行初始化
if ( self = [super initWithName:name andAge:age])
{
_no = no;
}
return self;
}
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Student *p = [[Student alloc] initWithName:@"Jim" andAge:29 andNo:10];
NSLog(@"00000");
}
return 0;
}
注意在以上的实例中:
对于student类中如果也对age name 这两个属性进行直接初始化是不恰当的(如以下代码):
- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no
{
if ( self = [super init] )
{
self.name = name;
self.age = age;
//[self setName:name];
//[self setAge:age];
}
return self;
}
二、分类
category 依赖于类 ,可以便于模块开发,增加开发的灵活,如多个人共同开发同一个类。通常情况下想要扩充类的方法,可以重新修改类增加方法,可以用继承,但是除了这两种方法外,还可以在不改变原来类的基础上扩充方法,就是分类。
1.格式:
分类的声明
@interface类名 (分类名称)
// 方法声明
@end
分类的实现
@implementation类名 (分类名称)
// 方法实现
@end
2.分类的应用:
扩展NSTring类,从下面的例子可以感受到分类的好处 ,一个庞大的类可以分模块开发,可以由多个人来编写,更有利于团队合作。 给NSString增加一个类方法:计算某个字符串中阿拉伯数字的个数
给NSString增加一个对象方法:计算当前字符串中阿拉伯数字的个数
NSTring+Number.h
#import <Foundation/Foundation.h>
@interface NSString (Number)
+ (int)numberCountOfString:(NSString *)str;
- (int)numberCount;
@end
NSTring+Number.m
#import "NSString+Number.h"
@implementation NSString (Number)
// @"abc434ab43"
+ (int)numberCountOfString:(NSString *)str
{
// 1.定义变量计算数字的个数
// int count = 0;
//
// for (int i = 0; i<str.length; i++)
// {
// unichar c = [str characterAtIndex:i];
//
// if ( c>='0' && c<='9')
// {
// count++;
// }
// }
// return count;
return [str numberCount];
}
- (int)numberCount
{
int count = 0;
for (int i = 0; i<self.length; i++)
{
// 取出i这个位置对应的字符
unichar c = [self characterAtIndex:i];
// 如果这个字符是阿拉伯数字
if ( c>='0' && c<='9' )
{
count++;
}
}
return count;
}
@end
3.注意:
分类单独放在一个文件里跟之前创建一个新类一样,分类的声明放在.h,分类的实现放在.m文件
分类可以访问原始类的实例变量,但不能添加变量,只能添加方法。如果想添加变量,可以考虑通过继承创建子类
可以实现原始类的方法,但不推荐这么做,因为它是直接替换掉原来的方法,这么做的后果是再也不能访问原来的方法
多个Category中如果实现了相同的方法,只有最后一个参与编译的才会有效
分类的优先级高于原来的类,在调用方法时,优先到分类中去查找,然后再去原来的类中找,之后再去父类中找。
若方法重写方法调用的优先级:分类(最后参与编译的分类优先)—>子类—>父类
三、类的本质
1.本质
类其实类也是一个对象,是Class类型的对象,简称“类对象”
Class类型的定义:typedef structobjc_class *Class;
类名就代表着类对象,每个类只有一个类对象
2 .+load
在程序启动的时候会加载所有的类和分类,并调用所有类和分类的+load方法
先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load
先加载元原始类,再加载分类
不管程序运行过程有没有用到这个类,都会调用+load加载
3. +initialize
在第一次使用某个类时(比如创建对象等),就会调用一次+initialize方法
一个类只会调用一次+initialize方法,先调用父类的,再调用子类的
4.获取类对象的2种方式
Class c = [Personclass];// 类方法
或者
Person*p = [Personnew];
Class c2 = [p class];// 对象方法
5.类对象调用类方法
Class c = [Person class];
Person *p2 = [c new];
四、description方法
使用NSLog和%@输出某个对象时,会调用对象的-description方法,并拿到返回值进行输出。NSLog(@"%@",p)默认情况下输出对象时结果是 :类名:内存地址,如果想把一个对象属性输出,就要重写description 。修改NSLog的默认输出,重写-description或者+description方法即可。1.+description重写:
+ (NSString *)description
{
return @"Abc";
}
那么在test2函数执行后就会输出Abc
void test2()
{
Class c = [Person class];
// 1.会调用类的+description方法
// 2.拿到+description方法的返回值(NSString *)显示到屏幕上
NSLog(@"%@", c);
}
2.NSLogs输出补充:
// 输出当前函数名
NSLog(@"%s\n", __func__);
// 输出行号
NSLog(@"%d", __LINE__);
// NSLog输出C语言字符串的时候,不能有中文
NSLog(@"%s", __FILE__);若文件名是中文就无法输出要使用 printf("%s\n", __FILE__);
// 指针变量的地址
NSLog(@"%p", &p);
// 对象的地址
NSLog(@"%p", p);
// <类名:对象地址>
NSLog(@"%@", p);