前言:最近没怎么做项目,除了学习Java的基础知识。空闲时间也通过读书巩固自己iOS的基础知识。特做了一些笔记(按自己理解的),分享出来方便查阅和学习。。。
No.1 成员变量
成员变量:
iOS: 成员变量指的是在类接口部分或类实现部分定义的变量,Object-C的成员变量都是实例变量,并不支持真正的类变量
iOS: 实例变量从该类的实例(对象)被创建开始起存在,直到系统完全销毁这个实例, 实例变量可以理解为实例成员变量,它作为实例的一个成员,与实例共存亡。
iOS: 实例变量(成员变量)无须显式初始化,只要一个类定义了实例变量,系统会默认初始化,基本类型的实例变量默认为0,指针类型的成员变量默认初始化为nil.
iOS: Object-C的static关键字,但这个static关键字不能用于修饰成员变量,它只能修饰局部变量,全局变量和函数。
1.static修饰局部变量表示该局部变量存储到静态存储区
2.static修饰全局变量用于限制该全局变量只能在当前源文件中访问
3.static修饰函数,说这个函数只能在当前源文件访问
Java: static修饰的为类变量
NO.2 单例最简单写法--单例完整版
1.最简单单例:
@interface JJTest : NSObject
//提供一个类方法创建对象
+(instancetype)shareInstance;
@end
//定义一个全局变量
static id instance = nil;
@implementation JJTest
+(instancetype)shareInstance{
//判断这个全局变量是否为nil,不为nil,就不用再创建这个实例了
if (instance == nil) {
//创建一个实例
instance = [[super alloc]init];
}
return instance;
}
//使用:
- (void)viewDidLoad {
[super viewDidLoad];
JJTest *test1 = [JJTest shareInstance];
JJTest *test2 = [JJTest shareInstance];
NSLog(@"test1的地址是:%@,test2的地址是:%@",test1,test2);
//这是最简单的单例类,不完整,如果我用alloc init创建的还是不一样的对象
JJTest *test3 = [[JJTest alloc]init];
JJTest *test4 = [[JJTest alloc]init];
NSLog(@"test3-%@,test4-%@",test3,test4);
}
NO.3 隐藏成员变量
@interface JJTest : NSObject
{
//通过private修饰符完全私有化实例变量
@private
NSString *_name;
NSInteger _age;
}
//写set和get方法
-(void)setName:(NSString *)name;
-(NSString *)name;
-(void)setAge:(NSInteger)age;
-(NSInteger)age;
@implementation JJTest
//写set和get方法
-(void)setName:(NSString *)name{
_name = name;
}
-(NSString *)name{
return _name;
}
//在set方法中对年龄作一些限制
-(void)setAge:(NSInteger)age{
if(age>0 && age<100){
_age = age;
NSLog(@"年龄正确");
}else{
NSLog(@"您给的年龄不符合实际");
}
}
-(NSInteger)age{
return _age;
}
//使用:
- (void)viewDidLoad {
[super viewDidLoad];
JJTest *test = [[JJTest alloc]init];
//set方法给age赋值
[test setAge:27];
test.age = 101;//(test.age = xxx) 点语法就相当于调用了setAge方法
//get方法获得age的值
NSInteger num = [test age];
NSLog(@"num=%ld",num);
}
NO.4 @property解读
1.从Objective-C 2.0版本开始,自动合成了setter方法和getter方法,并且生成一个同名的成员变量
2.虽然系统为我们提供了setter方法和getter方法,开发者依然可以提供setter或getter方法,这个方法就可以覆盖系统的setter或者getter方法
3.思考:
3.1:我们平时的Model作为自定义cell下的一个属性,常常通过重写model的setter方法
-(void)setModel:(Model *)model{
_model = model;
//拿到model给cell中的各个属性赋值
}
我们为什么没有写Model的setter方法的声明,因为声明在我们用@property定义model属性时候,系统已经为我们自动生成的setter和getter方法的声明和实现。 我们只需要重写setter方法的实现就好
3.2 cell.model = self.modelArr[indexPath.row];
cell.model = xxx 和 test.age = 101;是一样的,相当于调用了model实例变量(属性)的setter方法 ,就是给cell中的model赋值的一个过程
//4.简要说说点语法
对于@property定义的属性,自动合成setter和getter方法
cell.model 这是点语法,当调用点语法获取属性值的时候,调用的是getter方法, 当cell.model = XXX, 调用点语法给属性赋值的时候,相当于调用setter方法
NO.5 atomic 和 nonatomic
atomic:主要指在定义@property的时候,存取方法合成是否是原子操作,所谓原子操作: 主要指是否线程安全
1.如果使用 atomic,那么合成的存,取方法都是线程安全的,当一个线程进入存,取方法的方法体后,其它线程无法进入。 这样就可以避免多线程并发破坏对象的数据完整性。 虽然atomic可以保证对象数据的完整性,但atomic的线程安全会造成性能下降。
2.因此,大多数单线程环境下,我们都会考虑使用nonatomic来提高存取方法的访问性能
NO.6 使用copy修饰的用意
解读: 使用copy指示符,当调用setter方法对成员变量赋值时,会将被赋值的对象复制一个副本,在将该副本赋值给成员变量。
问题:对于一个NSString类型作为属性时,如果不用copy修饰,有时会出问题:
演示问题:
@interface JJTest : NSObject
//只定义一个name属性,不用copy修饰
/**name*/
@property (nonatomic , strong) NSString *name;
@end
- (void)viewDidLoad {
[super viewDidLoad];
JJTest *test = [[JJTest alloc]init];
//定义一个可变字符串
NSMutableString *str = [NSMutableString stringWithString:@"我是iOS菜鸟"];
//调用setter方法给这个test对象的name赋值
[test setName:str];
//获取name值打印一下
NSLog(@"第一次test.name=%@",[test name]);
//接下来改变这个str可变字符串的值
[str appendString:@",向着中高级程序员进发"];
//再来打印一次name值
NSLog(@"第二次test.name=%@",[test name]);
//打印结果: 第一次test.name=我是iOS菜鸟 / 第二次test.name=我是iOS菜鸟,向着中高级程序员进发
//出现的问题:我们并没有改变这个name的值,但是由于赋值给它的str是个可变字符串。 str和test.name都指向NSMutableString这个对象的地址,所以当我们通过str指向NSMutableString的地址,改变其值后,我们的test.name值也就改变了。
//这个问题应该是我们并不想看到的,所以这个时候我们就需要采取copy修饰符了
#解决问题:strong改为copy
@property (nonatomic , copy) NSString *name;
//打印结果:第一次test.name=我是iOS菜鸟/第二次test.name=我是iOS菜鸟
//从这次打印结果来看:
/*
当调用[test setName:str]方法时,程序会将str指向的NSMutableString对象复制一个副本,再将副本作为setName:的参数值,这样就可以实现当程序通过str修改底层NSMutableString对象时,test的name属性不会随着改变
*/
}
NO.7 自己重命名这个setter和getter方法
我们在用@property定义属性的时候,可以给setter和getter方法重命名
/**name*/
@property (nonatomic , copy, setter = tmac:, getter = kobe) NSString *name;
//使用:
JJTest *test = [[JJTest alloc]init];
//用我们重新命名的setter和getter方法给这个test的name属性赋值,取值
[test tmac:@"我是菜鸟"];
NSLog(@"test.name = %@",[test kobe]);
NO.8 readonly // readwrite
readonly修饰符:指系统只合成getter方法,不再合成setter方法
readwrite修饰符:setter和getter方法都合成,我们没有设置,系统自动为我们默认为readwirte.
NO.9 KVC简单的实现原理
setValue:forKey方法:
(1)程序优先考虑调用属性的setter方法完成设置
(2)如果该类没有setter方法,KVC机制会搜索该类名为_name的成员变量,无论成员变量是在类接口部分定义,还是在类实现部分定义的,也无论用哪个访问控制符修饰,KVC的底层代码实际上就是对_name成员变量赋值
//这也是我们为什么能通过KVC设置textField的提示文字的颜色
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
//因为对于textField来说, _placeholderLabel.textColor这个成员变量是被隐藏在类实现部分的(猜测),所以我们只能通过KVC赋值
(3)如果该类以上两个都找不到,KVC会搜索该类名为name的成员变量,无论成员变量是在类接口部分定义,还是在类实现部分定义的,也无论用哪个访问控制符修饰,这个KVC代码实际就是返回name成员变量的值
(4)如果以上都找不到,系统会执行该对象的valueforUndefinedKey方法
默认的valueforUndefinedKey:方法实现就是引发一个异常,这个异常将会导致程序因为异常结束
#举例:
@interface JJTest : NSObject
{
//定义一个name的成员变量和_name的成员变量
NSString *name;
NSString *_name;
}
@implementation JJTest
{
//类实现部分定义一个age属性
int age;
}
JJTest *test = [[JJTest alloc]init];
[test setValue:@"tmac" forKey:@"name"];
[test setValue:@"kobe" forKey:@"_name"];
[test setValue:@19 forKey:@"age"];
NSLog(@"name = %@",[test valueForKey:@"name"]);
NSLog(@"_name = %@",[test valueForKey:@"_name"]);
NSLog(@"age = %@",[test valueForKey:@"age"]);
其实可以看出区别的,对于name和_name是有差别的
NO.10 简单处理KVC操作属性赋值找不到属性的情况
我们在Test类中什么都不设置,然后我们直接来访问两个属性名
- (void)viewDidLoad {
[super viewDidLoad];
JJTest *test = [[JJTest alloc]init];
[test setValue:@"tmac" forKey:@"name"];
[test setValue:@19 forKey:@"age"];
NSLog(@"name = %@",[test valueForKey:@"name"]);
NSLog(@"age = %@",[test valueForKey:@"age"]);
}
//崩溃报错:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name.'
//我们在Test类实现部分重写KVC找不到key会调用的方法
@implementation JJTest
//重写setValue:forUndefinedKey:方法
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"您尝试设置的key:<%@>并不存在",key);
}
//重写valueForUndefinedKey:(id)key
-(id)valueForUndefinedKey:(id)key{
NSLog(@"您尝试访问的key:<%@>并不存在",key);
return nil;
}
@end