iOS 基础读书杂集(一)

前言:最近没怎么做项目,除了学习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


你可能感兴趣的:(iOS 基础读书杂集(一))