OC笔记2

//
//  main.m
//  testFoundation
//
//  Created by TAL on 2020/6/16.
//  Copyright © 2020 Song. All rights reserved.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        /*
            NSString
         */
        
        NSString *str1 = [NSString new];//这样创建的话是在堆区
//        NSString *str1 = @"rose";//这样创建的话是在常量区,数据段的。存储在常量区的数据不会被回收。
        // %p 打印变量的值;%@,打印指针指向的对象
        str1 = @"rose";
        NSLog(@"str1 = %@", str1);
        
        NSString *str2;
        NSString *s = @"12345";//这是我们最常用的创建对象的方式
        
        //最常用的类(+,静态)方法
        //1.+ (instancetype)stringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);  很重要。。。。=。=
        //构建字符串 “我是宋,我今年24”
        NSString *name1 = @"song";
        int myAge = 24;
        NSString* output = [NSString stringWithFormat:@"我是%@,我今年%d", name1, myAge];
        NSLog(@"%@", output);
        
        //常用的对象方法(length, characterATIndex, isEquealToString:(NSString *)aString;)
        //1.length
        NSString *name2 = @"jassica";
        NSUInteger lenOfName2 = [name2 length];
        NSLog(@"lenOfName2 : %lu", lenOfName2);
        //2.characterATIndex
        unichar indexOfName2 = [name2 characterAtIndex:2];
        NSLog(@"indexOfName2 = %C", indexOfName2);//大写的C
        //判断两个字符串是否相等不要用 == , 会出问题的。
        //3.isEquealToString:(NSString *)aString;
        NSString *name3 = [NSString stringWithFormat:@"jassica"];
        if([name2 isEqualToString:name3]) {
            NSLog(@"YES,他们是相等的。");
        }
        
        str2 = [str1 substringWithRange:NSMakeRange(0, 2)];
        int a = [[s substringWithRange:NSMakeRange(0, 2)] intValue];//强制类型转换。
        
        NSLog(@"str2 = %@", str2);
        
        NSLog(@"a = %i", a);
        
        /*
            NSMutableString
         */
        // NSMutableString为可变的字符串,与NSString不同的是,无论NSMutableString修改多少次
        //对象始终只有一个,每次修改字符串的时候,修改这个对象中的内容即可,不会再重新修改对象。
        //NSMutableString继承自NSString,并对NSString进行了扩展,同样用来存储字符串
        
        NSMutableString *str3 = [[NSMutableString alloc] init];
        
        [str3 setString:@"abc"];
        NSLog(@"str3 = %@", str3);
        
        [str3 appendString:@"345"];
        NSLog(@"str3 = %@", str3);
        
        [str3 appendFormat:@"name = %@, age:%d", @"liming", 25];
        NSLog(@"str3 = %@", str3);
        
        [str3 insertString:@" " atIndex:6];
        NSLog(@"str3 = %@", str3);
        
        [str3 replaceCharactersInRange:NSMakeRange(0, 3) withString:@"012"];
        NSLog(@"str3 = %@", str3);
        
        [str3 deleteCharactersInRange:NSMakeRange(3, 3)];
        NSLog(@"str3 = %@", str3);
        
        /*
            NSArray
         */
        //它只能存储oc对象,不能存储非oc对象,所以基本数据类型等非oc对象需要转化为oc对象才可以存储
        //这些对象在数组中有序存放,一个挨着一个
        //它是不可变的,一旦初始化完毕之后,里面的元素就是永远固定的,无法删除和新增?????!!!!
        // A: 所以NSMutableArray(是NSArray子类),也可以用来存储数据,唯一不同的是它是可变的,可以删除也可以动态增加。
        
        //一.互相转换
        //1.不可变数组转可变数组,数据会丢失,不要这样做,一般是mutable转其父类才对,没有父类转子类一说。
//        NSArray *arr = [NSArray array];
//        NSMutableArray *mArr = [NSMutableArray arrayWithArray:arr];
        
        //2.可变转不可变
//        NSMutableArray *mArray = [NSMutableArray array];
//        NSArray *a = [NSArray arrayWithArray: mArray];
        
        //二.NSArray
        NSArray *ary1 = [[NSArray alloc] init];
        NSArray *ary2 = [NSArray array];
        
        NSArray *ary3 = [NSArray arrayWithObject:@"abcdefg"];
        
        NSArray *ary4 = [NSArray arrayWithObjects:@"aa", @"bb", @"cc", @"dd", nil];
        
        //新语法
        NSArray *ary5 = @[@"abcdefg"]; // 和3等价
        NSArray *ary6 = @[@"aa", @"bb", @"cc", @"dd"]; // 和4等价
        NSArray *ary7 = [NSArray arrayWithArray:ary2];
        //从本地文件中读取数组
        NSArray *ary8 = [NSArray arrayWithContentsOfFile:@""];
        //从网络文件中读取数组
        NSArray *ary9 = [NSArray arrayWithContentsOfURL:[NSURL URLWithString:@""]];
        
        //数组元素个数
        NSUInteger count = ary6.count;
        //访问ary6第一个元素
        NSString *str = [ary6 objectAtIndex:0];
        for(int i = 0; i < ary6.count; i++) {
            NSLog(@"数组中第%d个元素为:%@", i, [ary6 objectAtIndex:i]);
        }
        //通过数组中的元素找到对应的下标
        NSUInteger index = [ary6 indexOfObject:@"aa"];
        NSLog(@"index = %lu", (unsigned long)index);
        //判断数组中是否存在某元素
        BOOL flag = [ary6 containsObject:@"bb"];
        
        //NSMutableArray和它差不多,随用随查吧
        
        
        
        /*
            集合(NSSet) 和 数组(NSArray) 有相似之处,都是存储不同的对象的地址;不过NSArray是有序的,NSSet是无序的集合
         */
        //集合是一种哈希表,运用散列算法,查找集合中的元素比数组速度更快  但是它 无序
        NSSet* set = [[NSSet alloc] initWithObjects:@"one", @"two", @"three", @"four", @"a", @"b", @"c", @"d", nil];
        //直接类名调用,不用alloc。 + 号方法使用一组对象创建新的集合
        // + 号 方法 是类方法,可以直接用类名来调用
        NSSet* set1 = [NSSet setWithObjects:@"a", @"b", @"c", @"d", nil];
        // 一 号方法 是实例方法,必须用类的实例来调用
        NSSet* set2 = [[NSSet alloc] initWithObjects:@"1", @"2", @"3", nil];
        
        
        // mutable set
        
        //初始化
        NSMutableSet *mutableSet = [[NSMutableSet alloc] init];
        [mutableSet addObject:@"1"];
        
        //加号方法使用一组对象创建新的集合
        NSMutableSet *mutableSet1 = [NSMutableSet setWithObjects:@"1", @"2", @"3", nil];
        
        //初始化一个新分配的集合,大小为size,减号方法,动态方法
        NSMutableSet *mutableSet2 = [[NSMutableSet alloc] initWithCapacity:3];
        [mutableSet2 addObject:@"1"];
        [mutableSet2 addObject:@"b"];
        [mutableSet2 addObject:@"3"];
        //创建一个有size大小的新集合
        NSMutableSet *mutableSet3 = [NSMutableSet setWithCapacity:3];
        [mutableSet3 addObject:@"a"];
        [mutableSet3 addObject:@"2"];
        [mutableSet3 addObject:@"c"];
        
        
        
        /*
            NSDictionary & NSMutableDictionary
         */
        //两种创建方式
        NSDictionary *dict1 = [NSDictionary dictionaryWithObjectsAndKeys:@"xx_cc", @"name", @"18", @"age", nil];//value:key
        NSDictionary *dict2 = @{@"name":@"xx_cc", @"age":@"18"}; //key:value
        
        //NSMutableDictionary *mDict = [NSMutableDictionary dictionaryWithCapacity:3];
        //[mDict setObject:@"a" forKey:@"name"];
        NSDictionary *mDict = @{@"name":@"song", @"age":@5};
        //NSMutableDictionary *mDict = @{@"name":@"song", @"age":@5};//这样是不好的,应该用定长度的?因为会warning
        
        //轮询
        NSEnumerator *enumeratorkey = [mDict keyEnumerator];
        for(NSObject *obj in enumeratorkey) {
            NSLog(@"key为: %@", obj);
            NSLog(@"通过key找到value值为:%@", [mDict objectForKey:obj]);
        }
        
        /*
            NSNumber
         */
        //很像java
        //NSArray和NSDictionary都无法存储基本数据类型,所以NSNumber就是用来将基本数据类型转化为对象的
        //基本数据类型存入数组需要先将基本数据类型转化为对象,然后存入数组或者字典中。
        
        //1.包装基本数据类型
        NSNumber *intNumber = [NSNumber numberWithInteger:100];//int integer区别?
        NSNumber *floatNumber = [NSNumber numberWithFloat:24.58];
        NSNumber *boolNumber = [NSNumber numberWithBool:YES];
        
        //字面量语法
        NSNumber *intNum = @1;
        NSNumber *floatNum = @2.5f;
        NSNumber *doubleNum = @3.14159;
        NSNumber *boolNum = @YES;
        NSNumber *charNum = @'a';
        
        int x = 5;
        float y = 6.32f;
        NSNumber *expressionNum = @(x * y);
        
        // 封装完后存入数组
        NSArray *arrrr = @[intNumber, floatNumber, boolNumber];
        
        // 解包
        NSInteger intValue = [intNumber integerValue];
        float floatValue = [floatNumber floatValue];
        
        /*
            NSValue
         */
        //我们常常遇到NSRange、CGPoint、CGSize、CGRect这些结构体,他们的变量没有办法存储到集合之中,因此需要先将这些结构体变量存储到OC对象中,再将OC对象存储到集合之中,NSValue类的对象就是用来包装结构体变量的。
        
        /*
            NSDate
         */
        //将服务器返回的时间进行一些处理。或者与当前时间进行计算,然后显示。
        
    }
    return 0;
}


/*   2020/06/17   */


/*
 匿名对象:
        假如我们有个Person类的话
    正常使用:
        Person *p1 = [Person new];
    匿名使用:
        [Person new]->_name = @"jassica";
        [Person new]->_age = 18;
        [[Person new] sayHi];
    !!!!!: 注意只能使用一次匿名对象,后面的匿名对象和前面不是一个东西,且无效。
    用途:
        1.如果某个对象的成员只会被我们使用一次,之后就不需要了,那么就可以使用匿名对象。
        2.
 */


/*
封装~~~@@@@@#@#@@¥@¥!#@#@¥@¥@¥@¥@
   一. 私有属性   很重要;
    假如说我们有个类,在main里我们给他赋值,age = 200,然而这是不对的,人 0 ~ 120差不多
    所以我们可以在main里进行判断来确定赋值与否。但是这是不好的。
    我们可以对类里面的age属性 私有化, 进行private,不让外界随意访问修改,所有的判断等在类内进行操作才可以。
   1. !!!!!:
    所以我们可以在类里面写方法,专门为属性来赋值,我们仔细看看,其实我们之前都是这么做的。只是当时我没有get到她这么做的意义何在。
    注意!这个方法一定是对象方法,因为我们是为这个属性进行赋值的。而且没有返回值。我们只是为age赋值,仅此而已。
    这个方法我们起名字很重要。一定是setAge,约定俗成。这个方法一定是有参数的。
    在这个方法的实现当中来进行判断,比如说之前的就是这个人的年纪是不是在0 ~ 200, 超过就不给赋值了。如果不符合逻辑则做默认处理。
    外界想要为对象赋值,则会调用这个方法去做。去验证,验证成功则去做,否则就不去做。
    
    eg!!@!!#!#!
    Person *p1 = [Person new];
    [p1 setAge:560];//对象方法哦, - 号 方法。
 
   2. 同时我们赋值之后还要get~~~ getAge(没有参数,返回类型为age的类型),这两个方法很关键,注意都是对象方法。~~~~~~
    
   3. 什么时候需要为属性封装setter 和 getter
    1). 只要属性需要被外界访问 那么就需要setter和getter,哪怕赋值和取值的时候没有任何逻辑验证。因为这是规范~!!
    2). 如果属性只在类的内部访问,那么就不需要为它封装 getter 和 setter

   4. 只读封装和只写封装
    只读封装的方法只提供getter 不提供 setter
    只写封装的方法只提供setter 不提供 getter
 */


/*
 对象和对象之间的关系::::
    1. 组合关系
        Computer: CPU, Memory, MainBoard...etc..
    2. 依赖关系
        人 依赖于 电话,   参数: 耦合度~  高耦合,低耦合(靠着低耦合设计类更优秀),高内聚(做自己的事情),(单一职责)
    3. 关联关系
         人 和 狗, 人有一条狗,拥有的关系
    4. 继承关系
 */


//-----------------------------------------------------------------------

/*   2020/06/18   */




/*
oc中的static:
    a.不能修饰属性,也不能修饰方法。
    b.但是可以修饰方法中的局部变量:变成静态局部变量,存储到常量区,不会回收,下次再执行的时候不用再声明了。所以只会初始化一次
    {
        static int num = 12; //没有static,每次我们调用这个方法都是初始化为12,但是用了static, 后面就不初始化,上次加到多少就是多少了。
        num++;
            }
    c.如果方法的返回值是当前类的对象,那么返回值就写成instancetype
    + (intancetype) student {
        return [Student new];
    }
 */

/*
 self 关键字
    看 Person.m 里的说明
    可以在对象方法和类方法中使用。
    self是一个指针,在对象方法中self指向当前对象。在类方法中self指向当前类
 
    1. self 在对象方法中,指向当前对象
 
    当前对象:谁调用方法就是当前对象
   
    2. 在对象方法中,self指向当前对象,何用?
 
     a.可以使用slef显示的访问当前对象的属性
        self->属性,代表访问的是当前对象的属性。
     b.可以使用self来调用当前对象的其他的对象方法。
    
    3. 对象方法中使用self的场景
     a. 必须使用self的场景
        在方法中存在和属性同名变量的时候,不用self直接访问的是局部变量。加上self访问的是内部属性了。
        
        在对象方法中,如果要调用当前对象的其他的对象方法,必须使用self
 
     b. 选用self的场景
        在方法中不存在和属性同名的局部变量。如果这个时候想要访问当前对象的属性,这个都是访问方法的属性。
 
        所以我们要求属性以下划线开头,局部变量不要以下划线开头就可以避免a中的第一种情况了。
 
    4. 类方法中使用self
     a. 类加载,当类第一次被访问的时候,会将类的代码存储在代码区
                代码区中用来存储类的空间也有一个地址
     b. 在类方法中,self也是一个指针,指向当前这个类在代码段中的地址
        self 在类方法中就相当于是这个类
     c. 总结一下取到类在代码段中的地址的方式
        1. 调试查看对象的isa指针的值
        2. 在类方法中查看self指针的值
        3. 调用对象的对象方法class 就会返回这个对象所属的类在代码段中的地址
        4. 调用类的类方法class
     d. 可以在类方法中使用self来显示的调用本类的其他的类方法。
        建议:如果要在当前类方法中调用本类的其他的类方法,虽然可以直接使用类名,但是还是建议使用self
 
     e. 在对象方法中,self表示当前对象,
        所以可以通过self访问当前对象的成员
        
        但是在对象方法中,不能使用self去调用本类的类方法
 
     f. 在类方法中,self表示当前的类
        所以可以通过self调用当前类的其他的类方法
 
        但是不可以使用self访问对象的成员,不能直接去访问属性和调用对象方法。
 
    5. 对象方法可以声明多次,但是只会认为有一次
        对象方法如果有多次声明只能实现1次,否则就会报错
 
        对象方法之间是不能重名的
        
        类方法之间不能重名
        
        但是对象方法和类方法之间是可以重名的;
        通过类名来调用,就是类方法
        通过对象名来调用,就是对象方法
 */


/*
 继承
    解决的问题:
        代码冗余。
        维护成本太高,以后需要修改一个变量,都要变化。
    
    继承的目的:
        子类像具有父类的所有的成员,但是不想自己去定义,而是凭空拥有。
 
    继承的语法:
        @interface 类名:父类的名称
            
        @end
    
    继承的效果:
        子类拥有了父类的所有属性和方法

     在新创建子类时候指定父类就行了。
    
 
 oc中的继承特性 :
    1. 一个类只能有一个父类,不能有多个父类
    2. 传递性
 
 NSObject
    是Foundation框架中的类,在这个类中有一个类方法new
    这个方法是用来创建对象的, 这个方法的返回值是创建的这个对象的指针。
    
    也就是说,如果要创建类的对象,就必须要调用这个new方法。 NSObject 就相当于 源。
 
    所以要求:所有类必须直接或者间接的从NSObject类继承,如果不继承,则毫无意义

    在NSObject类之中,还定义了一个属性,这个属性叫做isa指针,所以每个类之中都有一个叫做isa的指针。
 
 
*/



/*
  super:特指子类从父类继承过来的。主要是可读性高,其实用self都可以,但是还是super可读性高。
    1. 可以用在类方法和对象方法之中。
    2. 在对象方法中可以使用super关键字调用当前对象从父类继承过来的对象方法。
    3. 在类方法中可以使用super关键字调用当前类从父类继承过来的类方法
    4. super只能调用方法,不能访问属性
 */

/*
 访问修饰符:
    用来修饰属性,用来限定对象的属性在哪个范围可以访问。 = =。。private,public,protected(本类和其子类可以访问)
    private的话子类也不可以访问。这就是private和protected的区别。  package:可以在当前框架中访问。
    如果不限定修饰符的话,那么默认的就是protected。
 
 
    即使父类的属性有private,子类是可以继承的!!!,但是无法直接访问而已@#!¥@¥
 
    oc中不要用public,无论什么时候
    希望只在本类中使用,用private,如果希望子类使用,可以protected,推荐使用默认的protected
 
    只能修饰属性,不能修饰方法。
 
 
 真正的私有属性
    如果private的话,外界是不能访问,但是外界可以有提示。
    想真正的使用的话私有,不让外界看见的话。。。可以写在实现的.m文件中。。不写在.h文件中,这样就实现了真正的私有
    只要写在了实现文件中,那么无论怎么着都是绝对私有属性的, 各种访问修饰符都是无效,外界根本无法提示。
 真正的私有方法
    只写实现 不写声明就可以了。。。~~,那么这个方法就是真正的一个私有方法,不能再外界调用了。
 
 */



/*

    1.里氏替换原则 LSP
        子类可以替换父类的位置,而且程序的功能不受影响。  父类指针指向子类对象~!@!#@
        为什么可以?  父类指针迫切需求一个父类对象,而我们给了一个子类对象,因为子类就是父类的扩展,子类只可能多。
    
    2.LSP的作用
        1). 一个指针不仅可以存储本类对象地址,还可以存储子类对象的地址。
        2). 如果一个指针的类型是NSObject类型,那么这个指针中可以存储人一的oc对象的地址
        3). 我们写一个数组指针NSObject *obj[3]. 那么他可以存任何的子类对象指针 obj[0] = [Person new] obj[1] = [Student new]
        4). 当一个父类指针指向一个子类对象的时候,只可以调用父类中的成员,也就是说子类所独有的不可以访问。
        5). 左边是编译,右边是运行
    
多态:

    
*/


/*
 方法的重写
    a). oc中只要将子类中书写一个与父类具有相同的方法名,返回类型和参数,就可以覆盖重写
    b). oc中方法不可以重载
    c). oc不支持多重继承
 */
 
/*
 description方法
    a). 作用是打印对象,对于一个类,如果没有重写description方法,输出是该类的地址。
    b). 自定义类用NSLog是输出不了的,输出的是它的地址,而在后面实现了description后,在输出的时候,%@这个格式控制符会自动调用description函数
- (NSString *) description {
    NSString *str = [NSString stringWithFormat:@"%@, %d",self.name, self.age];
    return str;
    }
    c). 就像java 里面的 toString()  方法。
    d). 什么时候需要重写它?
        a: 如果希望使用%@ 打印一个对象的时候,而且这个对象打印的是我们希望的样子的时候。

 */

//-----------------------------------------------------------------------



/*   2020/06/19  */


/*
 子类在内存中的存储和调用
    在onenote里面了
 
 1. 子类对象中有自己的属性和所有父类的属性
 
 2. 代码段中的每一个类都有一个叫做isa的指针,这个指针指向他的父类,一直指到NSObject
 
    [p1 sayHi]
    *p1 -> p1 对象 -> 然后根据对象的isa指针找到person类,  如果有执行, 如果没有的话根据类的isa的指针找父类
    一直找到NSObject,如果没有就报错
    
 */

/*
 oc中的结构体和类的区别
    1.oc结构体只能封装数据,类还可以封装行为。
    2.应用场景,如果表示的这个实体,如果不仅由多个数据组成,还有行为,那么用类
    3.结构体分配在栈(局部变量),而对象分配在堆
        a.如果属性较多,不要定义成结构体,反而影响效率
    4.赋值
        结构体 student
        类 person
            student s1 = {"",""};
            student s2 = s1;
 
            person *p1 = [person new];
            person *p2 = p1;
 
    
 */

/*
oc中类的本质
    1.堆 栈 bss段 数据段 代码段
    2.代码段用来存储代码,类加载,当类第一次被访问的时候,这个类就会被加载到代码段存储起来。
 question ?
    1.类什么时候加载到代码段?
        类第一次被访问的时候就会被加载到代码段。
    2.类以什么样的形式存储在代码段?
        1).先在代码段中创建一个Class对象,它是Foundation框架中的类,这个Class对象就是存储类信息的。
        2).将类的信息存在Class对象当中
        3).所以类是以Class对象的形式存在代码段的。有isa指针,指向存储父类的类对象
    3.类一旦加载到代码段之后什么时候回收?
        是不会被回收的,除非程序结束。
    4.如何拿到存储在代码段中的类对象?
        调用person的类对象class    Class c1 = [Person class]这样就得到了地址,不需要加*,因为定义已经加了
 
        调用对象的对象方法,就可以得到存储这个对象所属的类的class对象的地址
    5.如何使用类对象?
        c1 完全等价于 Person, 所以只能是调用类方法。
        1.是用类对象来调用类的类方法,因为类对象就代表存储在这个类对象中的类。
            [Person sayHi] = [c1 sayHi]
        2.所以在使用Person的地方完全可以使用c1代替。
        3.可以使用类对象来调用new方法,创建存储在类对象中的类的对象
        
 */

/*
selector 选择器
    SEL 是一个数据类型,所以在内存中申请空间存储数据。
    SEL 其实是一个类,SEL对象是用来存储一个方法的。
 1.类是以Class对象的形式存储在代码段之中
    类名:存储这个类的类名,NSString
    还要将方法存储在类对象之中,如何将方法存储在类对象之中,
    1).先创建一个SEL对象。
    2).将方法的信息存储在这个SEL对象之中。
    3).再将这个SEL对象作为这个对象的属性。
 2.拿到存储方法的SEL对象
    
    SEL s1 = @selector(sayHi);(不需要加*,因为typedef的时候已经加了,和class一样)
    这有什么用呢? 得到的是存储方法的SEL对象地址
 3.调用方法的本质
    Person *p1 = [Person new];
    [p1 sayHi];   这一步内部的原理是:
                        1). 先拿到存储sayHi方法的SEL对象,也就是拿到存储sayHi方法的SEL数据
                        2). 将这个SEL消息发送给p1对象
                        3). 这个时候,p1对象接收到这个SEL消息以后,就知道要调用方法
                        4). 根据对象的isa指针找到存储类的类对象
                        5). 找到这个类对象之后,在这个类对象中去搜寻是否有和传入的SEL数据相匹配的。如果有就执行,如果没有再找父类,直到NSObject
 OC最重要的1个机制:消息机制
        调用方法的本质其实就是为对象发送SEL消息。
        [p1 sayHi]; 为p1对象发送一条sayHi消息。
    1.方法是以SEL对象的形式存储起来
    2.如何拿到存储方法的SEL对象
 4.手动的为对象发送SEL消息
    Person *p1 = [Person new];
    
    SEL s1 = @selector(sayHi);
    [p1 performSelector:s1];  与  [p1 sayHi]; 效果一样
    所以调用一个对象的方法有两种方式:1.[对象名 方法名]; 2.手动的为对象发送SEL消息。
    1. 注意事项:如果方法有参数,那么方法名是带了冒号的。
               如果方法有参数,如何传递参数。
    SEL s1 = @selector(earWithFood:);
    [p1 performSelector:s1 withObject:@"红烧肉"];
 
    [p1 performSelector:s1 withObject:ps];
 */


/*
 点语法
    OC当中也可以使用点语法访问对象的属性。
    和Java C#完全不一样
        OC的对象如果要为属性赋值或者取值,就需要调用对应的getter和setter
    使用点语法来访问对象的属性
        语法:
            对象名.去掉下划线的属性名;
        p1.name = @"Jack";//这个时候就会将@"Jack"赋值给p1对象的_name属性
        p1.age = 18;
        p1.gender = GenderMale,
 1. 点语法的原理:
    .就相当于setter或者getter 方法,注意是. 不是->
    p1.age = 18;
    这句话的本质不是把18直接赋值给p1对象的_age属性
 
    点语法在编译器编译的时候,会将点语法转换为setter或者getter的代码。
 
 注意:::
        1.在set和get方法里面慎用点语法,否则小心会递归出不来,死循环!@!#!@!
        2.如果我们的set和get方法不符合规范则我们的点语法就会报错@¥@¥
        3.如果属性没有封装set和get,是无法使用点语法的。
 */


/*
@property
    1. 解决的问题,我们每次定义一个属性之后,都要写set个get的重复操作,那么property就来了。
    2. 作用: 自动生成get和set的 “声明”,写在@interface里
    3. 语法:
        @property 数据类型 名称;
        @property int age;
    4. 原理:
        编译器在编译的时候,会根据@property生成getter和setter方法的实现。
        @property 数据类型 名称;
        生成为:
        - (void) setAge:(int)age;
        - (int) age;
    5. 注意事项:
        1). 类型和属性的一致,然后属性的名字去掉下划线。不要乱写。一定要符合规范!@!#!#
        2). @property的名称决定了生成的getter和setter的方法的名称
            @property的数据类型决定了生成的setter方法的参数类型和getter方法的返回值类型。会
 
    
 */


/*
 @synthesize
    
    @property只能生成getter和setter的声明,实现还是要我们自己来。
    而实现也是没有任何的技术含量
 
    1.作用:自动生成getter和setter方法的实现。
    2.语法:
        @synthesize @property名称
        
        @interface Person : NSObject
        {
            int _age;
        }
        @property int age;
        @end
 
 ----------------------------------------------------------------------------
 
        @implmentation Person     等价  @implmentation Person {
                                            int age;
        @synthesize age;                }
        @end                            - (void)setAge:(int) age {
                                            self->age = age;
                                        }
                                        @end
    3.@synthesize做的事情
        a. 生成一个真私有的属性,属性的类型和对应的@property类型一致
            属性的名字也一致
        b. 自动生成setter方法的实现,无逻辑验证
        c. 自动生成getter方法的实现
 
    4.希望@synthesize不要生成私有属性了。因为他的命名是不符合规范的
        getter和setter的实现中操作我们已经写好的属性了。
         
    语法: @synthesize @property名称 = 已经存在的属性名;
          @synthesize age = _age;
          这时候不生成私有属性。
          直接生成setter getter的实现,把参数的值直接赋值给指定的属性
    这才是我们希望的。
 
    5.注意:
        a). 如果直接写一个@synthesize
            @synthesize name;
        b). 如果指定操作的属性
            @synthesize name = _name;
        c). 生成的setter方法实现当中,是没做任何逻辑验证的。是直接赋值的
            生成的getter方法中,也是没有做任何逻辑验证的, 是直接返回的。
            
            如果需要自己的逻辑验证,则需要自己重写
        d). 批量声明
            如果多个类型一致的话,则可以批量声明。
            @property float weight, height;
            这种情况下类型不一致也可以的。因为这里本来也不声明属性的类型
            @synthesize name = _name, age = _age, weight = _weight, height = _height;
            
 */

/*
    这种写法是Xcode4.4之前的写法,之后做了一个增强
    @property 增强
        只需要写一个 @property, 编译器就会自动生成
        1). 生成私有属性, 带下划线的
        2). 生成getter setter的声明
        3). 生成getter setter的实现
    @property NSString *name;
 
    !使用注意:
        1. @property 的类型一定要和属性的类型一致
            名称要和属性的名称一致,只是去掉下划线。
        2. 可以批量声明。
        3. property生成的方法实现没做任何逻辑验证。
            if necessary 可重写需要的setter 和 getter
           但是只能重写一个,不可以同时重写。如果同时重写了,就不会生成那个私有属性了。
        4.如果想为类写一个属性,并且为这个属性封装setter和getter
            1个@property搞定
        5.继承
            父类的@property可以被子类继承
            但是生成的属性是私有的,在子类的内部无法直接访问生成的私有属性,但是可以通过getter和setter来访问
            [super setName:@""];
            [self setName:@""];
            self.name = @"";
 */

/*
  静态类型和动态类型
    OC是一门弱语言:编译器在编译的时候,语法检查的时候没有那么严格,不管怎么写都是可以的。
        比如说 int num = 12.12; 这也行。。
    优点:灵活~
    缺点:太灵活,程序执行的时候就报错。。
 
 1. 静态类型:
    一个指针指向的对象是一个本类对象
    Animal *a1 = [Animal new];
 2. 动态类型:
    一个指针指向的对象不是本类对象;(多态)
    Animal *a1 = @"jack";
    NSLog(@"%@", a1); // 这样打印出来的就是jack,没有任何问题。
    有点像cpp动态绑定???不是很确定。
 3. 编译检查
    编译器在编译的时候,能不能通过一个指针去调用指针指向的对象的方法?
    判断原则:
        看这个指针所属的类型之中是否有这个方法,如果则认为可以调用,编译通过
        如果这个类中没有这个方法,则编译报错。
        所以在编译的时候,能不能调用对象的方法主要是看 指针的类型。
        看这个:::
            Animal *a1 = [Pig new];
            [a1 eat]; //这样是编译不了的,因为eat是 Pig类的方法
            [(Pig *)a1 eat]; //这样就可以编译过了。但是运行的时候肯定会错误。
 4. 运行检查
    编译检查只是骗过了编译器,但是这个方法究竟能不能执行还不一定。
    运行的时候,运行的时候会去检查这个对象是否真正的有这个方法,如果有就执行。
        Pig *p = [Animal new];
        [p run]; //这个没问题
        [p eat]; //这个编译没问题,但是运行的时候有问题,因为p指向的是animal,但是animal中没eat方法。
    
 
 总之:编译看左边!!!!运行看右边!!!!
 
 */

/*
NSObject
    是oc所有类的父类,根据LSP,NSObject可以指向任意的OC对象。
    所以,NSObject是一个万能指针。
    NSObject *obj1 = [Person new];
    [obj1 sayHi];// 这样是不行的,NSObject中没有sayHi
    [(Person *)obj1 sayHi];// 这样是okay的,因为Person中有sayHi方法。
    
缺点:如果要调用指向的子类对象的独有的方法,必须要做类型转换。
 所以有了id指针。
 */

/*
id指针
    万能指针。
    1.typedef 类型, 定义的时候已经加了* 所以声明的时候不用加*了
    2.万能指针,任意的对象都可以指。
    
    id id1 = [Person new];
    id id2 = [Student new];
    id id3 = @"Jack";
 */

/*
NSObject 和 id 的异同
    相同点:
        万能指针,都可以指向任意的OC对象。
    不同点:
        1. 通过NSObject指针去调用对象的方法的时候,编译器会做编译检查
            但是id指针的话,无论怎么调用,编译器都会通过。
            !!!!!虽然id指针这么棒,但是id不可以使用点语法,且只能调用对象的方法 !!!
            
            NSObject *obj = [Person new];
            [obj sayHi];
            
            id id1 = [Person new];
            [id1 sayHi];
 */


/*
  instancetype  好好理解
    父类中的类方法创建一个父类对象返回
        1. 如果返回值写为父类类型的,那么子类来调用这个方法得到的就是父类指针。
            解决方法:把返回值改为id类型的。
        2. 方法的内部创建的对象 不要写死,因为写死创建的对象就固定了。
            我们希望那一个类来调用这个方法就创建那一个类的对象。
            
            把类名写为self, 那一个类来调用这个方法,self就指的是那一个类,创建的就是那一个类的对象。
 
            Person *p1 = [Person person];
            
            Student *p2 = [Student person];//如果我们想实现这个的话,就得按照下面右边的写。
                                           //不然返回的是Person的person方法,而不是Student的
 
 
 + (Person *) person                    + (id) person
 {                                      {
    return [Person new];    应该改为           return [self new];
 }                                      }
 
 但是还是有问题:
            NSString *str = [Person person]; // 这样的话,因为返回值是id类型的,所以不会报错,但是是绝对错误的。
                                            //编译器连个警告都没有:因为id不会做检查。所以吧返回值写成instancetype
                                            // 那么代表方法的返回值是当前这个类的对象。
                                            + (instancetype) person
                                            {
                                                return [self new];
                                            }
                                                    
        3. 方法的返回值是id类型的,问题是任意指针都可以接这个返回值,编译器连个警告都无
            
            如果方法的返回值是instancetype
            代表方法的返回值是当前这个类的对象。
 


 
        4. id和instancetype区别!@#!@¥
            1. instancetype 只能作为方法的返回值,不能在别的地方使用
                id既可以声明指针变量 也可以作为参数,也可以作为返回值
            2. instancetype 是一个有类型的,代表当前类的对象。
                id是一个无类型的指针,仅仅是一个地址,没有类型的指针。
 
    使用建议:!!!@
        1. 如果方法内部是在创建当前类的对象,不要写死成类名 [类名 new]  而是写成[self new]
        2. 如果方法的返回值是当前类的对象,也不要写死了,而是写instancetype
    
 */

// -------------------------------------------------------------------------------------
/*                 06/21                  */

/*
 动态类型检查
    因为编译器检查的是指针的类型,所以我们有可能会出错误。
    比如:
        NSString *str = @"jack";
        [(Person)* str sayHi];//这样的话是可以骗的过编译器的,但是明显我们这样做是不对的
    //如果想避免这种情况,可以采用下面这样的形式
        Person* p1 = [Person new];
        BOOL b1 = [p1 respondsToSelector:@selector(sayHi)];
        if(b1 == YES) {
            [p1 sayHi];
        }
    // isKindOfClass:
    // 还有其他的
 */

/*
 构造方法概述
    1. new实际上是一个类方法:+ (instancetype) new (new内部是先调用alloc 再调用init方法)
        创建对象
        初始化对象
        把对象的地址返回。
    2. alloc方法是一个类方法,作用:申请空间。哪一个类调用这个方法,就创建那个类的对象,并把对象返回。
    3. init方法是一个对象方法,作用:初始化一个对象。
    PS: 上面可以联想到cpp中的new的 std::operator 和 初始化对象
    Person *p1 = [Person new];
    Person *p1 = [[Person alloc] init]; //这两句完全等价
    
 
 */

/*
 init方法
    1.作用:初始化对象,为对象的属性赋初始值,这个init方法我们叫做构造方法。
        init方法做的事情:初始化对象。
        为对象的属性赋默认值
        
        基本数据类型 赋值 0
                    c指针 : NULL
                    oc指针 : nil
    2.所以,我们创建一个对象但是没有为这个对象的属性赋值,这个对象的属性值有默认值的。
    3.所以我们每次创建一个对象,都是已经为他初始化了。
 
    4.我们想要为创建的对象的属性的默认值不是 NULL, nil, 0
        那么我们需要重写init方法。
        那么这个时候我们就可以重写init方法,在这个方法中赋值。
    5. 重写init方法的规范:
        a. 必须要先调用父类的init方法,然后将方法的返回值赋值给self
        b. 调用init方法初始化对象有可能会失败,如果初始化失败,返回的就是nil
        c. 判断父类是否初始化成功,判断self的值是否为nil,如果不为nil,则初始化成功
        d. 返回self
 规范如下:::
 - (instancetype) init {
    if(self = [super init]) {
        self.name = @"jack";
    }
    return self;
 }
    6. questions
        1. 为什么调用父类的init方法
            a:因为父类的init方法 会初始化父类的属性,所以必须要保证当前对象中的父类 属性同时被初始化;
        2. 为什么赋值给self
            a:因为调用父类的init方法 会返回初始化成功的对象
               实际上返回的就是当前对象
        3. 什么时候需要重写init方法:
            如果希望创建出来的对象的属性不是nil null 0 而是我们的指定值
 
    7. 重写init方法存在的问题
        这样每次创建出来的对象的属性的值都是一样的
        我们希望:创建对象的时候,对象的属性的值由人来指定,而不是写死在init方法中
 
        解决办法
        自定义构造方法:
        
        规范:
        1). 自定义构造方法的返回值必须是instancetype
        2). 自定义构造方法的名称必须是initWith开头
        3). 方法的实现和init的要求一样
 
    - (instancetype) initWithName:(NSString *) name andAge:(int) age;
 
    - (instancetype) initWithName:(NSString *) name andAge:(int) age
    {
        if(self = [super init]) {
            self.name = name;
            self.age = age;
        }
        return self;
    }
 
    这样的话我调用就不能[Dog new];
    应该是:
    Dog *d1 = [[Dog alloc] initWithName:@"xiaohuang" andAge:2;
 */


//--------------------------------------------------------------------------------------
/* 内存管理 */


/*            06/22       */


/*
    1. 每一个对象都有一个属性,叫做retianCount,叫做引用计数器,是 unsigned long占据八个字节
        引用计数器的作用:用来记录当前这个对象有多少人在使用他,有点像cpp的use_count
    2. 当多一个使用的时候,应该先让引用计数器➕1,发送retain消息
    3. 少一个人用的时候,就引用计数器➖1,发送release消息
    4. 当引用计数器变为0的时候,系统就会自动回收。
    5. 对象被回收的时候会自动调用dealloc
 MRC:手动引用计数,手动内存管理
    手动发送retain和release
 ARC:自动引用计数,自动内存管理
    系统自动在合适的地方发送retain release
    
 重写dealloc,[super dealloc]放在最后一行。调用父类
 */

/*
    1.什么时候发送retain?
        当多一个人使用这个对象的时候。应该先为这个对象发送retain消息。
    2.什么时候发送release?
        当少一个人使用这个对象的时候,应该先为这个对象发送release消息。
 在ARC 机制下, retain release dealloc 这些方法无法调用。
 
    3. 内存管理的原则
        1.有对象的创建,就要匹配一个release
        2.有一个retain 就有一个release,次数匹配。
        3.谁用谁retain 谁不用谁release
        4.只有在多一个人用的时候才retain
 */

/*
    对象回收,僵尸对象
        对象回收的本质
            1. 申请一个变量名,实际上就是向系统申请指定字节数的空间,这些空间系统就不再分配给别人了‘
            2. 当变量被回收的时候,代表变量所占用的字节空间从此以后系统可以分配给别人使用了
            3. 但是字节空间中存储的数据还在
 
        回收对象:
            1. 所谓的对象的回收,指的是对象占用的空间可以分配给别人
            2. 当这个对象占用的空间没有分配给别人之前,其实对象的数据还在
 
    僵尸对象:
            被释放的对象,但是对象所占的空间还没有分配给别人。这样的对象就叫做僵尸对象
    
    我们通过野指针访问僵尸对象的时候,有可能有问题,有可能没问题。
 
    当僵尸对象占用的空间还没有分配给别人的时候,这是可以的
    但是当僵尸对象占用的空间非配给别人的时候,这就是不可以的
    
只要对象成为了僵尸对象,无论如何就不要访问了。如果访问的是僵尸对象,无论如何就报错。
 
    xcode 中的实时检查机制,打开之后,只要访问的是僵尸对象,无论空间是否分配,就会报错。
    但是打开这个机制的话,是很消耗性能的。
 
 1. 如何避免僵尸对象错误? 当一个指针成为野指针后,将这个指针的值设置为nil
 2. 无法复活一个僵尸对象。就是已经是僵尸对象了,retain无效。
 
 */

/*
    setter方法进行内存管理
        在MRC下:
    -(void) setCar:(Car* ) car {
        if(_car != car) {
            [_car release];
            _car = [car retain];
        }
    }
    还要重写dealloc方法:
    -(void) dealloc {
        [_car release];
        [super dalloc];
    }
    如果属性的类型不是oc类型的话,不需要上面这样
*/

/*
 property参数
  1.@property 可以带参数
    @property(参数1, 参数2, 参数3......) 数据类型 名称;
  2.和@property相关的四组参数
    a.与多线程相关的两个参数
        atomic  nonatomic
    b.与生成的setter方法的实现相关的参数
        assign retain
    c.与生成只读/读写相关的参数
        readonly readwrite
    d.是与生成的getter setter方法名字相关的参数
        getter setter
 
 a. 与多线程下相关的参数
    atomic : 默认值,如果写atomic,这个时候生成的setter就会生成线程安全锁
            特点:安全,效率低
    nonatomic: 如果写它 代码就不会加安全锁
            特点:不安全,但是效率高
    建议:使用nonatomic,无多线程之前都用它
b. 与生成的setter方法的实现相关的参数
    assign:生成的setter方法实现就是直接赋值
    retain:生成的setter方法的实现就是标准的MRC内存管理代码
            也就是:先判断新旧对象是否为同一对象,如果不是,release旧的,retain新的
    原则:oc对象:retain
        非oc对象:assign
    千万注意:
        retain参数,只是生成标准的setter方法为标准的MRC内存管理代码,不会自动的再dealloc方法
        所以需要自己手动的在dealloc中release
c. 与生成只读/读写的封装
    readwrite:默认值,会同时生成setter和getter
    readonly:只会生成getter,不会生成setter
 
d. 与生成getter 和 setter相关的参数
    默认情况下,@property 生成的getter setter方法的名字都是最标准的名字
    其实我们可以通过参数来指定生成的参数的名字
    getter = getter 方法名字 用来指定getter名字
    setter = setter 方法名字, 用来指定setter名字,注意,setter方法是带参数的,所以要加冒号
    在使用了修改之后的名字的话,在使用点语法的时候,编译器会转换为修改之后的名字,不会出错的。
 
    1. 无论什么时候都不要修改setter方法的名字,因为默认情况下生成的名字已经是最标准的了
    2. BOOL类型的话,getter方法可以改成 isGoodman, 加一个is,可以提高阅读性
     
 */

/*
 @class
    1.当两个类相互包含的时候,这个时候就会出现循环引用,就会造成无限递归,导致无法编译通过
    2.解决方案:将 #import "Book.h" 写成
                @class Book; 来标注这是一个类,这样子就可以在不引入对方头文件的情况下,告诉编译器这是一个类
 
    3.@class 和 #import 区别
        import 是将指定的文件的内容拷贝到写指令的地方
        @class 并不会拷贝任何内容,只是告诉编译器,这是一个类,这样编译器在编译的时候才可以知道这是一个类
        
        在.h中用@class,在.m中import必须
 */

/*
 循环 retain
    1. 当两个对象相互引用的时候
    A对象的属性是B对象,B对象的属性是A对象。
    这个时候,如果两边都使用retain,那么就会发生内存泄漏
    2. 解决方案:1端使用retain,另外一端使用assign
    3. 使用assing的一端不在需要release
 */

/*
自动释放池
    
    1.存储oc对象,当池子销毁,自动发送release
    2.@autoreleasepool {
        
    }
    3.如何将对象存储到自动释放池之中
        在自动释放池中调用对象的autorelease方法,就会将这个对象存入到当前自动释放池之中
        这个autorelease方法返回的是对象本身
        写法 : 必须要调用对象的autorelease方法才可以存储到自动释放池之中。
        @autoreleasepool {
            Person *p1 = [Person new];
            [p1 autorelease]; // 必须要这句话,才能将p1对象存储到自动释放池之中
        }
        1. 自动释放池结束的时候,仅仅是对存储在自动释放池中的对象发送一条release消息,而不是销毁对象。
 */

/*
ARC
    强指针,弱指针。
    _strong, _weak
    如果对象没有强指针指向的话,那么就会被立即释放。
 */


/*
 block 是一个数据类型
    1.block是一个数据类型,所以我们可以声明一个block类型的变量
    2.block类型的变量中专门存储一段代码,这段代码可以有参数,可以有返回值。
    
 */

/*
 foundation 框架
 */
/*
 NSString
    1. NSString 本质是一个类
        快速创建 NSString *str1 = @"rose";
        a. @"rose"  本质是一个对象,存的是 "rose"
        b. 将这个字符串对象的地址返回值赋值给str1 指针
    2. NSString的恒定性
        1.当我们使用简要的创建字符串对象的时候,也就是使用一个oc字符串常量来初始化字符串指针的时候
            这个字符串对象是存储在常量区的。
            NSString *str = @"jack";
        2.当我们调用NSString 的类方法来创建对象的时候
            
            NSString *str = [NSString stringWithFormat:@"jack"];
            NSString *str = [NSString new];
            创建的对象是在堆;
    3. 当在内存中创建和一个字符串对象的时候,这个字符串对象的内容就无法更改
        当我们重新为字符串指针初始化值的时候,并不是修改原来的字符串对象
        而是重新的创建一个字符串对象,将这个字符串对象的地址重新赋值给字符串指针变量。
    4. 当系统准备在内存创建一个字符串对象的时候,会先检查内存中是否有相同内容的字符串对象
        如果有,直接指向,如果没有才会重新创建。
    5. 存储在常量区的数据不会被回收。
 
    6. 使用频率最高的几个方法
        int age = 10;
        NSString *str = @"jack";
        1.拼接
        NSString *newStr = [NSString stringWithFormat:@"大家好,我叫%@,我今年%d岁", str, age];
        2.获取长度
        NSUInteger len = newStr.length;
        3.得到字符串中指定下标的字符
        NSString *str = @"jack is dog";
        unichar ch = [str characterAtIndex:12];
        4.判断两个字符串内容是否相同
         == 的局限:如果我们要比较两个OC字符串的内容是否相同,不能用 ==
        因为 == 比较的两个字符串的地址是否相同,而我们要比较的是两个字符串对象的内容是否相同
        [str1 isEqualToString:str2];
        5. C语言的字符串转换OC的字符串
            stringWithUTF8String:
        6. oc转成c的
            const char *s = str.UTF8String;
    7. 字符串的读写
    8. 使用NSURL读写字符串
    9. 字符串的比较
        NSComparisonResult res = [str1 compare:str2]; // 枚举
        swith(res) { }
        int res = [str1 compare: str2 option:()];
    10. 判断字符串是否是以指定的字符串开头?
    BOOL res = [str1 hasSuffix:@".mp3"];
    11. 搜索子串
    NSRange range = [str rangeOfString:@"love"];
    //两个参数, length 和 location
    12. NSRange
        1. 是foundation框架中定义的一个结构体
        表示一个范围
    13. 字符串的截取
        NSString *newStr = [str substringFromIndex:3];
        NSString *newStr = [str substringToIndex:3];
        NSString *newStr = [str substringWithRange:NSMakeRange(2,3)];
    14. 字符串的替换/删除, 删除的原理就是替换为空串
        [str stringBtReplacing0ccuer......:@"" withString:@""]
        
 */

/*
 NSMutableString
    可变长度的string
    当我们for给NSString增加长度的时候,每次循环都会创建一个字符串,这样在for很多的时候就会
    耗费很长的时间。
    Q:如何让大批量的字符串拼接可以更快速的一点
        慢的原因:每次循环都创建了一个字符串对象,当然很慢了
    A:NSMutableString是NSString的子类。直接可以改存在内部的字符串数据,不会新建对象改地址的
    NSMutableString *str = [NSMutableString string];
    [str appendString:@"jack"];
    [str appendString:@"rose"];
    int age = 10;
    [str appendFormat:@"我今年%d岁了",age];
attention:!!!!
    不能按照下面的方式初始化
    NSMutableString *str = @"jack";
    因为右边是父类对象
使用建议:
    平时还是使用NSString ,效率高
    后者在做大批量拼接的时候才使用,五次以上
 */

/*
 NSArray
    是oc中的数组
 特点:
    1.只能存oc对象,任意的oc 对象
    2.长度固定,一旦创建完毕,长度固定,无法新增,无法删除
    3.紧密相连(地址连续?),下标访问
    4.元素的类型都是id类型的。
    5.不能存基础数据类型
 创建对象:
    a.错误:NSArray *arr1 = [NSArray new];
    这样的创建出来的元素的个数是0个,不行哦,所以没有任何意义
    b.没有意义:NSArray *arr = [NSArray arrayWithObject:@"jack"];
    c.最常用的创建NSArray
    NSArray *arr = [NSArray arrayWithObjects:@"jack", @"rose"....., nil];
    注意,最后必须加上nil
    d.创建数组的简要方式,比较好
    NSArray *arr = @[@"jack", @"rose", @"lili"];
arr[0], arr[1]
    6.arr.count : 元素个数
    
 
数组遍历
    1. 使用for
    for(int i = 0; i < arr.count; i++)
    2. 当存储的数据类型不一致,用id
    3. 使用block遍历
    [arr enumerateObjectsUsingBlock:];
字符串和数组的两个方法:
    - (NSString *) compoents....
    
 */

//------------------------------------------------------

/*           06/23               */

/*
 NSMutableArray
    它是NSArray的子类,它可以动态的进行增加和删除
    
 */

/*
NSNumber
    
 */
// ------------------------------------------
/*
  07/08
 */

/*
 block
    1. block 是一个数据类型
        1). block类型的变量中专门存储一段代码,可以有参数和返回值。
    2. block 变量的声明
        1). 虽然block变量中是用来存储一段代码的,但是一个block变量中并不是任意的一段代码都可以存进去,是有限定的。
            也就是说,在声明block变量的时候,必须要指定这个block变量存储的代码段是否有参数,是否有返回值。
            一旦指定以后,这个block变量中就只能存储这样的代码了。
            
            声明了一个block变量,返回值是void,参数是一个int类型的,这个时候,block中只能存储无返回值且有一个int参数的代码段。
            
        2). 声明block变量的语法格式:
            返回值类型(^block变量的名称)(参数列表);
            void(^myBlock1)(); 表示声明了一个block类型的变量叫做myBlock1 这个变量中只能存储没有返回值没有参数的代码段。
            int (^myBlock2)();
            int (^myBlock3)(int num1, int num2);
 
        3). 一旦指定之后,这个block变量中就只能存储这样的代码段了,其他格式的代码段无法存储。
    3. 初始化block变量
        1). 原理:写一个符合block要求的代码段,存储到block变量中就可以了。
            
        2). 代码段的书写格式:
            ^返回值类型(参数列表) {
               代码段;
            }
        3). 写一个无参数无返回值的代码段
            ^void() {
              NSLog(@"laa");
            };
            这个时候就可以赋值了
            myBlock1 = ^void() {
              NSLog(@"laa");
            };
            当然也可以在声明的时候直接初始化
            void(^myBlock1)() = ^void() {
              NSLog(@"das");
            };
    4. 赋值给block变量的代码段必须要符合block变量的参数要求,否则就会报错。
    
    5. 如何执行存储在block代码?
        myBlock1(); //这样就可以了。
        有参数的:
        int sum = myBlock3(10, 11);
    
    6. 关于block的简写
        1). 如果没有返回值,代码段的返回值可省略。声明block变量的返回值当然不可以省略。
        2). 如果没有参数,那么代码段的小括弧可以省略。声明的当然不可以省略。
        3). 所以一个代码段既没有参数,也没有返回值的,就只写^
        4). 声明block的时候,如果有指定参数,那么可以只写类型,不写参数名称
        5). 无论代码段是否有返回值,在写代码的时候,可以不写返回值类型,省略
            如果代码段没有返回任何数据,那么他会认为这个代码段是没有返回值的
            如果代码段return了,那么return什么类型他就认为是返回什么类型
        但是还是建议按照标准来写,提高可读性。
 
    7. 简化block使用typedef
        使用场景:将一个长类型定义为一个短类型
 
    8. 在block内部可以取定义在外部的值,包括定义在外部的局部变量和全局变量
        在block内部可以修改全局变量的值,但是不可以修改定义在外部的局部变量的值
        注意:如果想要在block里面修改局部变量就必须为 定义在外部的局部变量为 __block int num = 200;
        
    9. block作为函数的参数
 
    
 */
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353