OC基础 04

 

OC 基础04

 
1.@property基本概念
>1.@property是编译器的指令
>2.@property 用在声明文件中告诉编译器声明成员变量的的访问器(getter/setter)方法
这样的好处是:免去我们手工书写getter和setter方法繁琐的代码
@property基本使用
1.在@inteface中,用来自动生成setter和getter的声明
示例:
@property int age; // 就可以代替下面的两行
- (int)age;   // getter
- (void)setAge:(int)age;  // setter
 
@property编写步骤
1.在@inteface和@end之间写上@property
2.@property后面写上需要生成getter/setter方法声明的属性名称, 注意因为getter/setter方法名称中得属性不需要_, 
所以@property后的属性也不需要_.并且@property和属性名称之间要用空格隔开
3.@property和属性名字之间告诉需要生成的属性的数据类型, 注意两边都需要加上空格隔开
 
@property增强
自从Xcode 4.x后,@property可以同时生成setter和getter的声明和实现
 
@interface Person : NSObject
{
    int _age;
}
@property int age;
@end
 
@property增强注意点
默认情况下,setter和getter方法中的实现,会去访问下划线 _ 开头的成员变量。
@interface Person : NSObject
{
    @public
    int _age;
    int age;
}
@property int age;

@end

int main(int argc, const char * argv[]) {

    Person *p = [Person new];
    [p setAge:30];
    NSLog(@"age = %i, _age = %i", p->age, p->_age);

    return 0;
}
如果没有会自动生成一个_开头的成员变量,自动生成的成员变量是私有变量,
声明在.m中,在其它文件中无法查看,但当可以在本类中查看
@property只会生成最简单的getter/setter方法,而不会进行数据判断
Person *p = [Person new];
[p setAge:-10];
NSLog(@"age = %i", [p age]);
如果需要对数据进行判断需要我们之间重写getter/setter方法
     若手动实现了setter方法,编译器就只会自动生成getter方法
     若手动实现了getter方法,编译器就只会自动生成setter方法
     若同时手动实现了setter和getter方法,编译器就不会自动生成不存在的成员变量
 
 
@property修饰符
修饰是否生成getter方法的
     readonly 只生成setter方法,不生成getter方法
     readwrite 既生成getter 又生成setter方法(默认)
@property (readonlyint age;
指定所生成的方法的方法名称
     getter=你定制的getter方法名称
     setter=你定义的setter方法名称(注意setter方法必须要有 :)
@property (getter=isMarried)  BOOL  married;
说明,通常BOOL类型的属性的getter方法要以is开头
 
 
2.@synthesize
 @synthesize是一个编译器指令,配合@property使用,它可以简化getter/setter放的实现
 有了@property增强后 @synthesize 基本上不用
 
 
@synthesize后面告诉编译器,需要实现那个@property生成的声明
 
告诉@synthesize, 需要将传入的值赋值给谁和返回谁的值给调用者
 
 @synthesize age = _age;
 
 setter
getter实现中会访问成员变量_age
 
如果成员变量_age不存在,就会自动生成一个@private的成员变量_age
 
 @synthesize age;
 
 setter
getter实现中会访问@synthesize后同名成员变量age
 如果成员变量age不存在,就会自动生成一个@private的成员变量age
 
示例:
@implementation Person
 
@synthesize age = _age ; // 等同于以下

//    - (void)setAge:(int)age {
//        _age = age;
//    }
//
//    - (int)age {
//        return _age;
//    }
@end
 
 
3.id类型
         id == NSObject * ( 万能指针)
         id是动态数据类型
         NSObject * 
是静态数据类型
        
         id
是数据类型,并且是动态数据类型
         id 可以定义变量,作为函数的参数,作为函数的返回值
     
         默认情况下所有的数据类型都是静态数据
        
        
静态数据:
        
静态数据在编译时就知道变量的类型
        
知道变量中有哪些属性和方法,并在编译时就可以访问这些属性和方法
        
通过静态数据定义的变量,如果访问了不属于静态数据类型的的属性和方法,那么编译器就会报错
        
        
动态数据:
        
在编译的事后并不知道变量的真实类型,只有在运行时才知道真实类型
         通过动态数据定义的变量,如果访问了不属于动态数据类型的属性和方法,编译器不会报错,只有在运行时报错
 
                    通过静态数据类型定义的变量 , 不能调用子类特有的方法
         通过动态数据类型定义的变量 , 可以调用子类特有的方法 , 运行时报错 , 编译时不会报错
         通过动态数据类型定义的变量,可以调用私有方法
 
4.new实现原理
 
         完整的创建一个可用的对象 :Person *p=[Person new];
         new 方法的内部会分别调用两个方法来完成 3 件事情 :
         (1) 使用 alloc 方法来分配存储空间 ( 返回分配的对象 );
         (2)
使用 init 方法来对对象进行初始化。
         (3)
返回对象的首地址
        
       
苹果官方的描述
         This method is a combination of alloc and init. Like alloc, it initializes the isa instance variable of the new object so it points to the class data structure. It then invokes the init method to complete the initialization process.
         
    Person *p = [Person new];
 
         可以把 new 方法拆开如下 :
        
         (1)调用类方法+alloc分配存储空间,返回未经初始化的对象Person *p1=[person alloc];
         (2)调用对象方法-init进行初始化,返回对象本身 Person *p=[p1 init];
         (3) 以上两个过程整合为一句 :Person *p=[[Person alloc] init];
 
        Person *p1 = [Person alloc];
        Person *p = [p1 init];
       
   Person *p = [[Person alloc] init]; 等同于 Person *p = [Person new];
   
5.构造方法
          在OC中,以init开头的方法,我们称之为构造方法
 
重写构造方法
      为什么要重写?
当我们希望对象创建出来就拥有我们所希望的属性和方法,这个时候就需要重写构造方法
重写init方法格式:
 
- (id)init {
    self = [super init];
    if (self) {
        // Initialize self.
    }
    return self;
}
 
[super init]的作用:
 
面向对象的体现,先利用父类的init方法为子类实例的父类部分属性初始化。
self 为什么要赋值为[super init]:
 
简单来说是为了防止父类的初始化方法release掉了self指向的空间并重新alloc了一块空间。还有[super init]可能alloc失败,这时就不再执行if中的语句。
重写init方法其它格式
- (id)init {
     // 常见的方式
    if (self = [super init]) {
        // Initialize self.
    }
    return self;
}
 
构造方法使用注意:
子类拥有的成员变量包括自己的成员变量以及从父类继承而来的成员变量,在重写构造方法的时候应该首先对从父类继承而来的成员变量先进行初始化。
      原则:先初始化父类的,再初始化子类的。
               先调用父类的构造方法[super init];
               再进行子类内部成员变量的初始化。
千万不要把self = [super init]写成self == [super init]
重写构造方法的目的:为了让对象方法一创建出来,成员变量就会有一些固定的值。
 
 
instancetype的作用
instancetype与id相似,不过instancetype只能作为方法返回值,它会进行类型检查,如果创建出来的对象,赋值了不相干的对象就会有一个警告信息,防止出错。
// init此时返回值是id
NSString *str = [[Person alloc] init];
// Person并没有length方法, 但是id是动态类型, 所以编译时不会报错
NSLog(@"length = %i", str.length);

// init此时返回值是instancetype
// 由于instancetype它会进行类型检查, 所以会报警告
NSString *str = [[Person alloc] init];
NSLog(@"length = %i", str.length);

instancetype *p = [[person alloc] init];
// 错误写法instancetype只能作为返回值
 
 
自定义构造方法
有时候仅仅靠重写构造方法(初始化方法),不能满足需求。比如一个班级中不可能所有学生的年龄都一样,这时候我们需要在创建某个学生的时候能够传入这个学生的年龄。这时候就需要来自定义构造函数(初始化函数)
 
自定义构造方法的规范
(1)一定是对象方法,以减号开头
(2)返回值一般是instancetype类型
(3)方法名必须以initWith开头
 
示例:
 
@interface Person : NSObject
@property int age;

@property NSString *name;

// 当想让对象一创建就拥有一些指定的值,就可以使用自定义构造方法
- ( instancetype )initWithAge:(int)age;

- ( instancetype )initWithName:(NSString *)name;

- ( instancetype )initWithAge:(int)age andName:(NSString *)name;

@end
 
继承中的自定义构造方法
不能在子类访问父类私有变量
 
@interface Person : NSObject

@property int age;

- ( id )initWithAge:(int)age;
@end
 
@interface Student : Person

@property NSString *name;

- ( id )initWithAge:(int)age andName:(NSString *)name;
@end

@implementation Student

- ( id )initWithAge:(int)age andName:(NSString *)name
{
    if (self = [super init]) {
//        这个_Age是父类中通过property自动在.m中生成的无法继承,不能直接访问
//        _age = age;
        [self setAge:age];
        _name = name;
    }
    return self;
}
@end
父类的属性交给父类的方法来处理
@interface Person : NSObject

@property int age;

- ( id )initWithAge:(int)age;
@end
 
@interface Student : Person

@property NSString *name;

- ( id )initWithAge:(int)age andName:(NSString *)name;
@end

@implementation Student

- ( id )initWithAge:(int)age andName:(NSString *)name
{
    if (self = [super initWithAge:age]) {
        _name = name;
    }
    return self;
}
@end
OC基础 04_第1张图片
使用注意:
(1)自己做自己的事情
(2)父类的属性交给父类的方法来处理,子类的方法处理子类自己独有的属性
(3)自定义构造方法必须以intiWith开头,并且’W’必须大写
 
6.自定义工厂方法
类工厂方法是一种用于分配、初始化实例并返回一个它自己的实例的类方法。类工厂方法很方便,因为它们允许您只使用一个步骤(而不是两个步骤)就能创建对象. 例如new
 
自定义类工厂方法的规范
(1)一定是+号开头
(2)返回值一般是instancetype类型
(3)方法名称以类名开头,首字母小写
 
示例:
+ (id)person;
+ (id)person
{
    return  [[Person alloc]init];
}

+ (id)personWithAge:(int)age;
+ (id)personWithAge:(int)age
{
    Person *p = [[self alloc] init];
    [p setAge:age];
    return p;
}
 
苹果官方文档的类工厂方法
苹果在书写工厂方法时也是按照这样的规划书写
[NSArray array];
[NSArray arrayWithArray:<#(NSArray *)#>];
[NSDictionary dictionary];
[NSDictionary dictionaryWithObject:<#(id)#> forKey:<#(id<NSCopying>)#>];
[NSSet set];
[NSSet setWithObject:<#(id)#>];
子父类中的类工厂方法
由于之类默认会继承父类所有的方法和属性, 所以类工厂方法也会被继承
由于父类的类工厂方法创建实例对象时是使用父类的类创建的, 所以如果子类调用父类的类工厂方法创建实例对象,创建出来的还是父类的实例对象为了解决这个问题, 以后在自定义类工厂时候不要利用父类创建实例对象, 改为使用self创建, 因为self谁调用当前方法self就是谁
正确写法:
@interface Person : NSObject
+ (id)person;
@end

@implementation Person
+ (id)person
{
 
//     谁调用这个方法,self就代表谁
//    注意:以后写类方法创建初始化对象,写self不要直接写类名
    return  [[self alloc]init];
}
@end

@interface Student : Person
 
@end

@implementation Student
@end

int main(int argc, const char * argv[])
{
    Student *stu = [Student person];// [[Student alloc] init]
}
 
 
7.类的本质
1.类的本质其实也是一个对象(类对象)
2.程序中第一次使用该类的时候被创建,在整个程序中只有一份。
3.此后每次使用都是这个类对象,它在程序运行时一直存在。
4.类对象是一种数据结构,存储类的基本信息:类大小,类名称,类的版本,继承层次,以及消息与函数的映射表等
5.类对象代表类,Class类型,对象方法属于类对象
6.如果消息的接收者是类名,则类名代表类对象
7.所有类的实例都由类对象生成,类对象会把实例的isa的值修改成自己的地址,每个实例的isa都指向该实例的类对象
 
 
如何获取类对象
通过实例对象
 
格式:[实例对象   class ];
如:   [obj class];
 
通过类名获取(类名其实就是类对象)
格式:[类名 class];
如:[Person class]
 
类对象的用法
用来调用类方法
[ Person test];
 
Class c = [ Person class];
[c test];
 
用来创建实例对象
Person  *g = [ Person new];
 
Class c = [ Person class];
Person  *g1 = [c new];
类对象的存储
OC基础 04_第2张图片
 
8.SEL
SEL类型代表着方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系
每个类的方法列表都存储在类对象中
每个方法都有一个与之对应的SEL类型的对象
根据一个SEL对象就可以找到方法的地址,进而调用方法
 
SEL类型的定义
     typedef struct objc_selector *SEL;
 
首先把test这个方法名包装成sel类型的数据
根据SEL数据到该类的类对象中,去找对应的方法的代码,如果找到了就执行该代码
如果没有找到根据类对象上的父类的类对象指针,去父类的类对象中查找,如果找到了,则执行父类的代码
如果没有找到,一直像上找,直到基类(NSObject)
如果都没有找到就报错。
 
注意:
在这个操作过程中有缓存,第一次找的时候是一个一个的找,非常耗性能,之后再用到的时候就直接使用。
Dog *dog=[[Dog alloc] init];
[dog eat];

OC基础 04_第3张图片

 
SEL使用
定义普通的变量
     如:SEL sel = @selector(show);
作为方法实参与NSObject配合使用
检验对象是否实现了某个方法
     - (BOOL) respondsToSelector: (SEL)selector 判断实例是否实现这样方法
     + (BOOL)instancesRespondToSelector:(SEL)aSelector;
    BOOL flag;
    // [类 respondsToSelector]用于判断是否包含某个类方法
    flag = [Person respondsToSelector:@selector(objectFun)]; //NO
    flag = [Person respondsToSelector:@selector(classFun)]; //YES

    Person *obj = [[Person alloc] init];

    // [对象 respondsToSelector]用于判断是否包含某个对象方法
    flag = [obj respondsToSelector:@selector(objectFun)]; //YES
    flag = [obj respondsToSelector:@selector(classFun)]; //NO

    // [类名 instancesRespondToSelector]用于判断是否包含某个对象方法
    // instancesRespondToSelectorr只能写在类名后面, 等价于 [对象 respondsToSelector]
    flag = [Person instancesRespondToSelector:@selector(objectFun)]; //YES
    flag = [Person instancesRespondToSelector:@selector(classFun)]; //NO
让对象执行某个方法
    • - (id)performSelector:(SEL)aSelector;
    • - (id)performSelector:(SEL)aSelector withObject:(id)object;
    • - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
    Person *p = [Person new];
    SEL s1 = @selector(objectFun);
    [p performSelector:s1];

    SEL s2 =  @selector (objectFun:);
    [p performSelector:s2 withObject: @“屎泰龙" ];

    SEL s3 = @selector(objectFun:value2:);
    [p performSelector:s3 withObject:@" 屎泰龙 "  withObject: @“猪李叶" ];

    SEL s4 = @selector(classFun);
    [Person performSelector:s4];

    SEL s5 = @selector(classFun:);
    [Person performSelector:s5 withObject:@" 屎泰龙 "];

    SEL s6 = @selector(classFun:value2:);
    [Person performSelector:s6 withObject:@" 屎泰龙 " withObject:@" 猪李叶 "];
作为方法形参
@implementation Person

- (void)makeObject:(id) obj performSelector:(SEL) selector
{
    [obj performSelector:selector];
}
@end

int main(int argc, const char * argv[]) {

    Person *p = [Person new];
    SEL s1 = @selector(eat);
    Dog *d = [Dog new];
    [p makeObject:d performSelector:s1];

    return 0;
}

 
OC方法查找顺序
 
OC基础 04_第4张图片
OC基础 04_第5张图片
OC基础 04_第6张图片
 
1.给实例对象发送消息的过程(调用对象方法)
     >根据对象的isA指针去该对象的类方法中查找,如果找到了就执行
     >如果没有找到,就去该类的父类类对象中查找
     >如果没有找到就一直往上找,直到跟类(NSObject)
     >如果都没有找到就报错
2.给类对象发送消息(调用类方法)
     >根据类对象的isA指针去元对象中查找,如果找到了就执行
     >如果没有找到就去父元对象中查找
     >如果如果没有找到就一直往上查找,直到根类(NSOject)
     >如果都没有找到就报错

 

你可能感兴趣的:(OC基础 04)