Object-c的一些基本概念

自学了一个多月的IOS,对Object-C也有了初步的认识,也有很多观点不知道是否正确,所以整理了一下,和小伙伴们分享分享

1.OC中使用的消息机制代替调用方法

区别:使用消息结构的语言,其运行时缩引执行的代码是由环境来决定的,而函数调用的语言则是用编译器来决定的

2.对象等同性

判断两个对象是否想同,不要使用 ==操作符来判断,因为该操作符比较的是两个指针本身,而不是指针所指的对象,应该用NSObject协议中声明的isEquel方法来判断两个

对象的等同性!两个不同的对象总是不想等的!

1 NSString *oneStr = @"abc123";
2 NSString *twoStr = [NSString stringWithFormat:@"abc%i",123];
3 BOOL equalA = (oneStr == twoStr);//NO
4 BOOL equalB = [oneStr isEqual:twoStr];//YES
5 BOOL equalC = [oneStr isEqualToString:twoStr];//YES

判断两个对象等同性的关键方法
    (BOOL)isEqual:(id)object;
    @property (readonly) NSUInteger hash;

注意:

  • 若想监测对象的等同性,提供isEqual:与 hash 方法

  • 相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同

  • 不要盲目逐个检测每条属性,而是应该依照具体需求来制定监测方案

  • 编写 hash 方法时,应该是用计算速度快而且碰撞低的算法

 3.setter 方法 getter 方法

   setter 方法:
               》1.一定是对象方法
               》2.一定没有返回值
               》 3.方法名称一定以set开头,后面要设置的属性去掉下划线,首字母大写
               》 4.一定要有参数,参数的类型需要和设置的属性类型一致,并且参数名称就是属性名称去掉下划线
   getter 方法:
               》1.一定是对象方法
               》2.一定有返回值,且返回值类型一定与属性类型一致
               》 3.方法名称就是属性名称去掉下划线
               》4.一定没有参数

4.继承  

              》1,子类方法名和父类方法名相同,我们称之为方法重写
              》2,对象方法和类方法都可以重写
              》3,成员变量不能重写

              》何时使用继承?
                          XXX是xxx 或者 XXX is a xxx

             》 [super 方法名]/[self 方法名] 

                         supper 指定调用父类的方法                    

                         self:先在子类中查找,再在父类中查找
                         supper:直接在父类中查找

5.多态:父类指针指向子类对象

6.动态类型(id):在编译的时候只会检查当前类型对应的类中有没有需要调用的方法,在运行时,才会判断它的真实类型! 动态数据类型一般用于多态

7.构造方法

            》[Person new]和[[Person alloc]init]的区别

               他们两基本相同,只是 new初始化只能调用init方法,init初始化的时候能调用其他方法;

            》new 做了三件事

              1.开辟存储空间 +alloc方法
              2.初始化所有的属性(成员变量) -init方法
              3.返回对象地址

            》动态数据类型与静态数据类型的区别

8.重写init 方法

           》注意:必须按照苹果的规定格式来,否则会引发错误

             1.必须先初始化父类,再初始化子类
             2.必须判断父类是否初始化成功,只有父类初始化成功,才能继续初始化子类
             3.返回当前对象的地址

             4.instancetype只能作为返回值类型,不能作为形参和变量类型

             5.为什么不使用ID?因为ID是动态类型,程序在编译时不会检查类型,而在运行时检查,为了使程序编译时发现错误,使用了instancetype

 不带参数初始化:

1 -(instancetype)init 
2 {
3     self=[super init];//先初始化父类
4     if (self!=nil) {//必须判断父类是否初始化成功
5         _age=10;
6     }
7     return self;
8 }

带参数初始化

 1 //initWithAge W要大写 属性名称不要以new开头,否则有可能引发未知错误,方法名也不要以new开头
 2 -(instancetype)initWithAge:(int)age WithName:(NSString *)name
 3 {
 4     self=[super init];
 5     if (self!=nil) {
 6     _age=age;
 7     _name=name;
 8         }
 9     return self;
10 }

9.自定义类工厂方法

            》自定义类工厂方法是苹果的一个规范,一般情况下,我们会给一个类提供自定义构造方法和自定义类工厂方法用于创建一个对象

            》提供对象方法 叫做构造方法
            》提供类方法 叫做类工厂方法

1         Person*p=[Person person];
2         p.age=10;
3         NSLog(@"%i",p.age);
4         Person *p2=[Person personWithAge:100];
5         NSLog(@"%i",p2.age);
 1 /*
 2    》什么是类工厂方法:
 3    用于快速创建对象的类方法,我们称之为类工厂方法
 4    类工厂方法主要用于  给对象分配存储空间和初始化这块存储空间
 5  
 6    》规范:
 7    1.一定是类方法 +
 8    2.方法名称以类名开头,首字母小写
 9    3.一定有返回值,返回值是 id/instancetype
10  */
11 @interface Person : NSObject
12 @property int age;
13 //定义一个无参数类工厂方法
14 +(instancetype)person;
15 //带参数的类工厂方法
16 +(instancetype)personWithAge:(int)age;
 1 @implementation Person
 2 //实现无参数类工厂方法
 3 +(instancetype)person
 4 {
 5     return [[self alloc]init];//这里最好使用self来调用,否则有继承关系时会出错
 6     
 7 }
 8 +(instancetype)personWithAge:(int)age
 9 {
10    Person *p= [[self alloc]init];
11     p.age=age;
12     return p;
13 }
14 @end

10.类的本质

             》1. 类其实也是一个对象,这个对象会在这个类第一次被使用时创建

               2.只要有了类对象,将来就可以根据类对象来创建实例对象

                3. 实例对象中有一个isa指针,指向创建自己的类对象

             》 类对象中保存了当前对象所有的对象方法

               当给一个实例对象发送消息时,会根据实例对象中的isa指针去对应的类对象中查找

 1         Person *p1=[[Person alloc]init];
 2         Person *p2=[[Person alloc]init];
 3         //1.如何创建类对象
 4         //[实例对象 class],[类名 class]
 5         //一个类在内存中只有一个类对象
 6         Class c1=[p1 class];
 7         Class c2=[p2 class];
 8         Class c3=[Person class];
 9         NSLog(@"c1:%p,c2:%p,c3:%p",c1,c2,c3);//结果 c1:0x100001140,c2:0x100001140,c3:0x100001140
10         
11         //2.类对象的应用场景
12         //》1.用于创建实例对象
13         Person *p3=[[Person alloc]init];
14         //》2.用于调用类方法
15         [p3 test];

11.load和initialize方法何时加载

 1 /*
 2       只要程序启动,就会将所有类的代码加载到内存中,放到代码区
 3       load方法会在当前类被加载到内存的时候调用,有且只调用一次
 4       如果存在继承关系,会先调用父类的load方法,再调用子类的load方法
 5  */
 6 +(void)load
 7 {
 8     NSLog(@"类第一次被加载的时候调用");
 9 }
10 
11 //当前类第一次被使用的时候调用(也会是创建类对象的时候)
12 //initialize在整个程序的运行过程中只会调用一次,不管这个类被使用了多少次,都只会调用一次
13 //initialize用于对某个类一次性的初始化(如果有些东西只需要做一次,就把这些东西写在initialize里面)
14 //如果存在继承关系,会先调用父类的initialize方法,再调用子类的load方法
15 +(void)initialize
16 {
17     NSLog(@"类第一次被使用时调用了");
18 }

12.SEL

 1 int main(int argc, const char * argv[]) {
 2     @autoreleasepool {
 3         //》SEL类型的第一个作用,配合对象/类来检查对象/类中有没有实现某一个方法
 4         SEL sel=@selector(setMyage:);
 5         Person *p=[[Person alloc]init];
 6         //》判断person 中有没有setMyage:这个方法,如果有,就会返回true(1),如果没有,就会返回false(0)
 7         //》respondsToSelector 如果通过对象来调用该方法,则会判断对象中有没有实现-号开头的方法
 8         //》                   如果通过类来调用该方法,则会判断该类中有没有实现+号开头的方法
 9        BOOL b= [p respondsToSelector:sel];
10         NSLog(@"b:%i",b);
11         BOOL c= [p respondsToSelector:@selector(test)];
12         NSLog(@"b:%i",c);
13         BOOL d= [Person respondsToSelector:@selector(test)];
14         NSLog(@"b:%i",d);
15         //》2.SEL类型的第二个作用:配合对象/类来调用某一个SEL方法
16         [p performSelector:@selector(demo)];
17         //》withObject需要传递参数,
18         //注意:参数必须是对象类型,也就是说方法的形参必须是对象类型,因为withObject只能传递对象,否则会报错
19         //withObject最多只能传递2个参数
20         [p performSelector:@selector(demo:)withObject:@"123"];
21         
22         //》3.SEL类型的第三个作用:
23         //配合对象 将sel对象作为方法的形参
24         Car *car=[[Car alloc]init];
25         SEL selcar=@selector(run);
26         Person *pp=[[Person alloc]init];
27         [pp makeObject:car andSel:selcar];
28         
29     }
30     return 0;
31 }

 13.内存管理

               只要一个对象被释放了,我们就称为这个对象为“僵尸对象”

                  》当指针指向一个僵尸对象,我们就称这个指针为野指针
               》只要给一个野指针发送消息就会报错
               》 空指针:没有指向存储空间的指针 nil 0
               》为了避免给野指针发送消息会报错,一般情况下,当一个对象被释放后我们会将这个对象的指针设置为空指针
               》在OC中给空指针发送消息不会报错

               》当A对象使用B对象时,一定要对B对象进行一次retain,这样才能保证A对象存在时,B对象也存在,也就是说,无论什么时候,A对象都可以使用B对象

                  当A对象释放时,一定要对B对象进行一次release,这样才能保证A对象释放时,B对象也会被释放,避免内存泄露!

              》例如Person对象使用Room对象:

 1 #import "Person.h"
 2 
 3 @implementation Person
 4 -(void)setRoom:(Room *)room
 5 {
 6     if (_room!=room) {//判断传入的对象是否与当前对象一样
 7         [_room release];//release以前的对象
 8         //retain传入的对象
 9         _room=[room retain];//retain 不仅会让引用计算器+1,还会返回当前对象
10     }
11     
12     
13 }
14 -(void)dealloc
15 {
16     [_room release];
17     NSLog(@"%s",__func__);
18     [super dealloc];
19 }
20 @end

 14.分类-Category

        》方法分为:方法的声明,方法的实现,所以通过某个类扩充方法,也分为方法的声明和方法的实现 

        》分类的作用:

           1.可以在不修改原有类的基础上,给这个类扩充一些方法

           2.一个庞大的类可以分模块开发

           3.一个庞大的类可以由多个人来编写,更有利于团队合作

        》注意点:

          1.分类是给原有类扩充方法的,它只能添加方法,不能添加属性

          2.分类中使用@property,只会生成getter和setter的声明,不会生成实现,以及私有的成员变量

          3.可以在分类中访问原有类的.H文件

          4.一个类可以有很多分类

          5.如果在分类中有和原有类重名的方法,会先调用分类中的方法,也就是说会忽略原有类的方法

          6.如果多个分类都有重名的方法,那么执行谁是由编译器决定,谁最后被编译,就执行谁(在开发中尽量不要写重名的方法)

          7方法的调用顺序:分类-->子类-->父类

 15.Block    

       》1.block可以访问外界的变量
          2.block中可以定义和外界同名的变量,如果和外界定义的是同名的对象,那么在block访问的是Block中的变量
          3.默认情况下不可以在block中修改外界变量的值
            因为block和外界的变量不是同一个变量
            如果block中访问到外界的变量,block会将外界的变量copy一份到堆内存中
            因为block中使用的外界的变量是copy的所以调用之前修改外界的变量是不会影响到block中copy的值
         4.如果想在block中修改外界变量的值,必须在外界变量前加_block,

      》block是存储在堆中还是栈中?
         默认情况下是存储在栈中,如果对block进行copy,block会转移到堆中
        如果在栈中,block访问了外界的变量,那么不会对对象进行retain操作
        但如果在堆中,block访问了外界的变量,那么会对对象进行一次retain操作

 1  //block和函数一样,可以没有返回值也没有形参,也可以有
 2     //void(*roseBlock)();指向函数的指针
 3     void(^roseBlock)(int);//定义一个block
 4     roseBlock= ^(int num){
 5         for (int i=0; i<=num; i++) {
 6             printf("  {@}
");
 7             printf("   |
");
 8             printf("  \|/
");
 9             printf("   |
");
10         }
11     };
12     //要想执行block保存的代码,必须调用
13     roseBlock(10);
14      //无返回值,有参数
15     void(^testBlock)(int)=^(int num)
16     {
17         for (int i=0; i<num; i++) {
18             NSLog(@"---");
19         }
20     };
21     testBlock(10);
22     //有返回值,有参数
23     int(^sumBlock)(int,int);
24     sumBlock=^(int value1,int value2){
25         return value1+value2;
26     };
27     NSLog(@"sun:%i",sumBlock(10,20));

16:协议protocol

     协议的注意点:

       1.协议只能声明方法,不能声明属性
       2.父类遵守某个协议,子类也会自动遵守这个协议
       3.在OC中一个类可以继承多个协议
         但OC中的类只能有一个父类,也就是说OC中的类只有单继承(支持多层继承,不支持多继承)
      4.OC中的协议可以遵守其他协议

 1 #import <Foundation/Foundation.h>
 2 
 3 @protocol SportProtocol <NSObject>
 4 //注意:如果没有任何修饰符修饰协议中的方法,默认情况下是@required(必须实现) 如果没有实现,则会报警告
 5 //如果协议中的方法是@optional 如果遵守该协议的类,没有实现该方法,则不会报警告
 6 //@required  @optional仅仅是程序员之间的交流,不能控制是否一定实现该方法
 7 @required
 8 -(void)playFootball;
 9 -(void)playBaskball;
10 @optional
11 _(void)play;
12 
13 @end

 IOS交流群:5883244

原文地址:https://www.cnblogs.com/sl372/p/5217051.html