———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———
一、点语法
本质是方法的调用,用在setter和getter的方法调用中。
Person *p = [Person new];
p.age = 10; // [p setAge:10];
int a = p.age; // [p age];
二、成员变量的作用域
@private 在当前类的对象方法中访问
@protected 可以在当前类及其子类的对象方法中访问(默认)
@public 在任何地方都能直接访问对象的成员变量
@package 只要处在同一个框架中,就能直接访问对象的成员变量
三、@property和@synthesize
1、property
// 自动生成age的setter和getter方法,并自动生成@private类的_age变量可以自动生成某个成员变量的setter和getter方法(包括声明和实现)。
@property int age;
// - (void) setAge:(int)age;
// - (void)age;
使用注意:
1> 如果手动实现了setter方法,编译器就只会自动生成getter方法
2> 如果手动实现了getter方法,编译器就只会自动生成setter方法
3> 如果手动实现了setter和getter方法,编译器就不会自动生成不存在的成员变量
2、synthesize
//生成age的setter和getter方法的实现,并访问_age这个成员变量可以自动生成某个成员变量的setter和getter方法的实现。
@synthesize age = _age;
- (void)setAge:age
{
_age = age;
}
- (int)age
{
return _age;
}
//默认会访问age这个变量,若没有则自动生成@private类的age变量
@synthesize age;
四、id
id,万能指针,能指向、操作任何的OC对象,只适用于OC对象。
本质:typedef NSObject * id; // id == NSObject *
id后面不用加*。
注意:id类型不能使用点语法。
Person *p = [Person new];
p.age = 20;
id d = [Person new];
[d setAge:10];
五、构造方法
构造方法:用来初始化对象的方法,是个对象方法,以 - 开头。
1、new方法
[Person new]等价于[[Person allot] init]
Person *p =[Person new];
// 调用+allot方法分配存储空间,返回分配好内存的对象
Person *p1 = [Person allot];
// 调用-init方法初始化,返回已经初始化的对象
Person *p2 = [p1 init];
// 合并
Person *p3 = [[Person allot] init];
2、重写构造方法
为了让对象创建出来,成员变量就会有一些固定的值
1> 先调用父类的构造方法[super init];
2> 再进行子类内部成员变量的初始化。
@implementation Person
- (id)init
{
// 调用父类init方法:初始化父类中的一些成员变量和其他属性
// 若对象初始化成功,则继续对成员变量初始化
if (self = [super init])
// 返回初始化对象_age = 20;
return self;
}
3、自定义构造方法
1> 一定是对象方法,以 - 开头;
2> 返回值一般都是id类型;
3> 方法名一般以initwith开头。
#import <Foundation/Foundation.h>
// 父类
@interface Person : NSObject
@property NSString *name;
@property int age;
- (id)initWithName:(NSString *)name andAge:(int)age;
@end
@implementation Person
- (id)initWithName:(NSString *)name andAge:(int)age
{
if ( self = [super init] )
{
_name = name;
_age = age;
}
return self;
}
@end
// 子类
@interface Student : Person
@property int no;
- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no;
@end
@implementation Student
- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no
{
// 父类的成员变量交给父类去初始化
if ( self = [super initWithName:name andAge:age] )
{
_no = no;
}
return self;
}
@end
六、分类Category
1、分类的格式
1> 分类的声明
@interface 类名 (分类名称)
方法声明
@end
2> 分类的实现
@implementation 类名 (分类名称)
方法实现
@end
2. 分类的使用注意:
1> 分类只能增加方法,不能增加成员变量;
2> 分类方法实现中,可以访问原来类中声明的成员变量;
3> 分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法;
4> 方法调用的优先级:分类(最后参与编译的分类优先) > 原来的类 > 父类;
5> 分类和继承都是在不改变原来模型的情况下,给类扩充一些方法。
#import <Foundation/Foundation.h>
// 给NSString添加分类
@interface NSString (NumberCount)
+ (int)numberCountOfSttring:(NSString *)str;
- (int)numberCount;
@end
@implementation NSString (NumberCount)
// 类方法,计算某个字符串中数字的个数
+ (int)numberCountOfSttring:(NSString *)str
{
int count = 0;
// 遍历
for (int i = 0; i < str.length; i++)
{
//characterAtIndex:i 返回下标是i的字符
unichar c = [str characterAtIndex:i]
if ( c >= '0' && c <= '9' )
count++;
}
return count;
}
// 对象方法,计算这个字符串中数字的个数
- (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
七、类的本质
1、类对象
类也是一个对象,是Class类型的对象,简称“类对象”。
类对象的本质:typedef struct objc_class *Class;
类名就代表类对象,一个类只能有一个类对象。
Person *p = [[Person allot] init];
Class c = [p class];
// [Person test]
[c test];
获取类对象的2种方式
类方法:
Class c = [Person class];
对象方法:
Class c2 = [p class];
类对象调用方法
Class c = [Person class];
Person *p2 = [c new];
2、+load和+initialize
#import <Foundation/Foundation.h>
+ load
在程序启动时会加载所有的类和分类,并调用每个类和分类的+load方法。
加载顺序:先加载父类,在加载子类,最后加载分类。
不管程序有没有用到这个类,都会调用+load这个方法把类加载进来。
+ initialize
当第一次使用某个类时,就会调用当前类的+initialize方法,只会调用一次。
调用顺序:先调用(初始化)父类的,再调用(初始化)子类。
@interface Person : NSObject
@property int age;
+ (void)test;
@end
@implementation Person
+ (void)test
{ SLog(@"调用了test类方法");
}
// 当程序启动的时候,就会加载一次项目中所有的类,类加载完毕后就会调用+load方法
// 先加载父类,再加载子类
+ (void)load
{
NSLog(@"Person load");
}
N
// 当第一次使用这个类的时候,就会调用一次initialize方法
+ (void)initialize
{
// 监听
NSLog(@"Person initialize");
}
@end
3、description方法
1> - description方法
使用NSLog和%@输出某个对象时,会调用对象的-description方法,
并拿到返回值进行输出(类名:内存地址)
2> + description方法
使用NSLog和%@输出某个类对象时,会调用类对象+description方法,
并拿到返回值进行输出(类名)
3> 修改NSLog的默认输出
重写-description或者+description方法即可
4> 死循环陷阱
// 决定了实例对象的输出结果如果在-description方法中使用NSLog打印self
- (NSString *)description
{
// NSLog(@"%@", self);// 死循环
return [NSString stringWithFormat:@"age=%d, name=%@", _age, _name];
}
// 决定类对象的输出结果
+ (NSString *)description
{
return NSStringFromClass([self class]);
}
4、SEL
每个类的方法列表都封装在类对象中,
每个方法都有一个与之对应的SEL类型的对象,
根据这个SEL对象就能找到方法的地址,然后调用方法。
SEL类型的本质:typedef struct objc_selector *SEL;
1> SEL对象的创建
SEL s1 = @selector(方法名);
// NSString对象转换为SEL对象
SEL s2 = NSSelectorFromString(@"方法名");
2> SEL对象的使用
// 将SEL对象转换为NSString对象
NSString *str = NSStringFromSelector(@selector(方法名));
// 调用对象p的test方法:
Person *p = [[Person alloc] init];
[p performSelector:@selector(test)];
3> _cmd
_cmd 代表当前方法的SEL,每个方法内部都有这么一个变量。
- (void)test2
{
// 死循环
// [self performSelector:_cmd];
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"test2.., _cmd=%@", str);
}
5、NSLog输出增强
NSLog(@"%d", __LINE__);// 代码当前行号// NSLog输出C语言字符串的时候,不能有中文// NSLog(@"%s", __FILE__);// 输出不成功!(可以先做转换)printf("%s\n", __FILE__);// 当前文件路径NSLog(@"%s", __func__);// 当前函数名