您的位置:首页 > 移动开发 > Objective-C

OC基础 — 4

2018-01-18 15:40 15 查看
    - property和synthesize

    (1)@porperty是一个编译器指令

        在Xocde4.4之前,
可以使用@porperty来代替getter/setter方法的声明

        也就是说我们只需要写上@porperty就不用写getter/setter方法的声明 

        编译器只要看到@property,
就知道我们要生成某一个属性的getter/setter方法的声明

    (2)@synthesize是一个编译器指令,
它可以简化我们getter/setter方法的实现

        1 在@synthesize后面告诉编译器,
需要实现哪个@property生成的声明

        2 告诉@synthesize,
需要将传入的值赋值给谁和返回谁的值给调用者

        3 如果在@synthesize后面没有告诉系统将传入的值赋值给谁,
系统默认会赋值给和@synthesize后面写得名称相同的成员变量

    (3)增强的property: 

        从Xcode4.4以后apple对@property进行了一个增强,
不需要使用synthesize,只要利用一个@property就可以同时生成setter/getter方法的声明和实现

        没有告诉@property要将传入的参数赋值给谁,
默认@property会将传入的属性赋值给_开头的成员变量

    (4)@property有一个弊端:
它只会生成最简单的getter/setter方法的声明和实现,
并不会对传入的数据进行过滤

         如果想对传入的数据进行过滤,
那么我们就必须重写getter/setter方法

         如果不想对传入的数据进行过滤,
仅仅是提供一个方法给外界操作成员变量,
那么就可以使用@property

    (5)如果利用@property来生成getter/setter方法,
那么我们可以不写成员变量,
系统会自动给我们生成一个_开头的成员变量

   
    注意: @property自动帮我们生成的成员变量是一个私有的成员变量,
也就是说是在.m文件中生成的,
而不是在.h文件中生成的  

    - property修饰符

    (1)@property(readwrite)
int age;      //可读可写

              //注意:默认情况下,@property就是readw
194bc
rite的

        @property(readonly)
int age;       //只读

        @property(getter = get)
int age;   //重命名getter方法

        @property(setter = set:)
int age;  //重命名setter方法

    (2)程序员之间有一个约定,
一般情况下获取BOOL类型的属性的值,
我们都会将获取的方法名称改为isXXX

        @property(getter=isMarried)
BOOL married;

    - id类型

    (1)id是一个数据类型,
并且是一个动态数据类型

        可以用于:1
定义变量

                2 作为函数的参数

                3 作为函数的返回值

    (2)动态类型和静态类型

               静态数据类型的特点: 

               1
在编译时就知道变量的类型,
有哪些属性和方法

               2
在编译的时候就可以访问这些属性和方法, 

               3  如果是通过静态数据类型定义变量,
如果访问了不属于静态数据类型的属性和方法,
那么编译器就会报错

               4  默认情况下所有的数据类型都是静态数据类型

               动态数据类型的特点:

               1
在编译的时候编译器并不知道变量的真实类型,
只有在运行的时候才知道它的真实类型

               2
如果通过动态数据类型定义变量,
如果访问了不属于动态数据类型的属性和方法,
编译器不会报错

               3
动态类型的弊端:
由于动态数据类型可以调用任意方法,
所以有可能调用到不属于自己的方法,
而编译时又不会报错,
所以可能导致运行时的错误

               4
应用场景:
多态,
可以减少代码量,
避免调用子类特有的方法需要强制类型转换

               通过静态数据类型定义变量,
不能调用子类特有的方法

               通过动态数据类型定义变量,
可以调用子类特有的方法

               通过动态数据类型定义的变量,
可以调用私有方法   

    (4)id == NSObject *
万能指针

               id和NSObject *的区别:
NSObject *是一个静态数据类型,id
是一个动态数据类型

    (5)为了避免动态数据类型引发的运行时的错误,
一般情况下如果使用动态数据类型定义一个变量,
在调用这个对象的方法之前会进行一次判断,
判断当前对象是否能够调用这个方法

        
id obj = [Student
new];   

        
if ([obj
isKindOfClass:[Student
class]]) {

                 // isKindOfClass:判断指定的对象是否是某一个类,
或者是某一个类的子类

                [obj
eat];

         }

        
if ([obj
isMemberOfClass:[Student
class]]) {

                 // isMemberOfClass:判断指定的对象是否是当前指定的类的实例

                 [obj
eat];

         }

    - new方法与alloc方法

    (1)new做了三件事情:

        1 开辟存储空间(+ alloc
方法)

        2 初始化所有的属性(成员变量)(-
init 方法)

        3 返回对象的地址

    (2)alloc做了什么事情: 

        1 开辟存储空间

        2 将所有的属性设置为0

        3 返回当前实例对象的地址

       
//初始化成员变量,
但是默认情况下init的实现是什么都没有做

        Person *p1 = [Person
alloc]; 

        //返回初始化后的实例对象地址

        Person *p2 =  [p1
init];

        //注意: alloc返回的地址,
和init返回的地址是同一个地址

        //[[Person alloc] init]; == [Person new];

        //建议创建一个对象时都使用 alloc init,
这样可以统一编码格式

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

    - 构造方法

    (1)在OC中init开头的方法,
我们称之为构造方法

       
构造方法的用途:
用于初始化一个对象,
让某个对象一创建出来就拥有某些属性和值

       (2)重写init方法,
在init方法中初始化成员变量

        注意:
重写init方法必须按照苹果规定的格式重写,
如果不按照规定会引发一些未知的错误

             1 必须先初始化父类,
再初始化子类

             2 必须判断父类是否初始化成功,
只有父类初始化成功才能继续初始化子类

             3 返回当前对象的地址

      
- (instancetype)init

   {

       //初始化父类

      
//只要父类初始化成功,
就会返回对应的地址,
如果初始化失败,
就会返回nil

       //注意:
不要把 =
号写为 ==,一定要将[super init]的返回值赋值给self

       self = [super
init];

      
//判断父类是否初始化成功

       if (self !=
nil) {

           //初始化子类,设置属性的值

           _age =
6;  

       }

       // 4.返回地址

       return
self;

   }

    - instancetype与id的区别

       (1)instancetype == id ==
万能指针 ==
指向一个对象

        1 id在编译的时候不能判断对象的真实类型

          instancetype在编译的时候可以判断对象的真实类型

       
2  id和instancetype除了一个在编译时不知道真实类型,
一个在编译时知道真实类型以外,
还有一个区别

          id可以用来定义变量,
可以作为返回值,
可以作为形参

          instancetype只能用于作为返回值

       (2)如果init方法的返回值是instancetype,
那么将返回值赋值给一个其它的对象会报一个警告

        如果init方法的返回值是id,
那么将init返回的对象地址赋值给其它对象是不会报错的

        自定义构造方法时,
返回值尽量使用instancetype,
不要使用id

    - 自定义构造方法

       (1)自定义构造方法: 

        1 一定是对象方法

        2 一定返回id/instancetype

        3 方法名称一定以init开头

        4  一个类可以有0个或者多个自定义构造方法

        5  自定义构造方法可以有1个或多个参数

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

       {

          
if (self = [super
init]) {

              
_age = age;

              
_name = name;

       }

              return
self;

       }

    - 类工厂方法

       (1)用于快速创建对象的类方法,
我们称之为类工厂方法

        类工厂方法中主要用于给对象分配存储空间和初始化这块存储空间

 

       (2)规范: 1
一定是类方法

                        2
方法名称以类的名称开头,
首字母小写

                        3
一定有返回值,
返回值是id/instancetype

      
+ (instancetype)person

       {

      
    return [[Person
alloc] init];

       }

       //自定义类工厂方法

      
+ (instancetype)personWithAge:(int)age

       {

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

           p.age = age;

        
  return p;

       }

       (3)注意:
以后但凡自定义类工厂方法,
在类工厂方法中创建对象一定不要使用类名来创建,要使用self来创建

             self在类方法中就代表类对象,
谁调用当前方法, self就代表谁

    - 类的本质

       (1)类其实也是一个对象,
这个对象会在这个类第一次被使用的时候创建

        只要有了类对象,
将来就可以通过类对象来创建实例对象

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

       (2)类对象中保存了当前对象所有的对象方法

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

    //获取类对象

    Class c1 = [p1 class];      
//[实例对象 class];

    Class c2 = [Person
class];   //[类名 class];

       (3)类对象的应用场景:1
用于创建实例对象

                       2
用于调用类方法

    - 类的启动过程

       (1)只要程序启动就会将所有类的代码加载到内存中,
放到代码区

        load方法会在当前类被加载到内存的时候调用,
有且仅会调用一次

        如果存在继承关系,
会先调用父类的load方法,
再调用子类的load方法

    + (void)load

    {

        NSLog(@"Person类被加载到内存了");

    }

       (2)当当前类第一次被使用的时候就会调用initialize方法(创建类对象的时候)

        initialize方法在整个程序的运行过程中只会被调用一次,
无论你使用多少次这个类都只会调用一次

        initialize用于对某一个类进行一次性的初始化

        initialize和load一样,
如果存在继承关系,
会先调用父类的initialize再调用子类的initialize

    + (void)initialize

    {

        NSLog(@"Person initialize");

    }

    - SEL类型

   
  1 SEL类型的第一个作用:配合对象/类来检查对象/类中有没有实现某一个方法

        SEL sel =
@selector(setAge:);

        Person *p = [Person
new];

        BOOL flag = [p
respondsToSelector:sel];

        /* 判断p对象中有没有实现-号开头的setAge:方法

           如果P对象实现了setAge:方法那么就会返回YES

           如果P对象没有实现setAge:方法那么就会返回NO
*/

       
respondsToSelector注意点:
如果是通过一个对象来调用该方法那么会判断该对象有没有实现-号开头的方法

                                
如果是通过类来调用该方法,
那么会判断该类有没有实现+号开头的方法

        SEL sel1 =
@selector(test);

        flag = [p respondsToSelector:sel1];      
//判断该对象有没有实现-号开头的方法

        flag = [Person
respondsToSelector:sel1]; 
//判断该类有没有实现+号开头的方法 

   
  2 SEL类型的第二个作用:配合对象/类来调用某一个SEL方法 

   

        SEL sel =
@selector(demo);

        Person *p = [Person new];

        //调用p对象中sel类型对应的方法

        [p performSelector:sel];

    

        SEL sel1 =
@selector(signalWithNumber:);

        [p performSelector:sel1
withObject:@"13838383438"]; 

        // withObject: 需要传递的参数

        /* 注意:
如果通过performSelector调用有参数的方法,
那么参数必须是对象类型, 

               
也就是说方法的形参必须接受的是一个对象,
因为withObject只能传递一个对象 */

        // 注意:performSelector最多只能传递2个参数

        SEL sel2 =
@selector(sendMessageWithNumber:andContent:);

        [p performSelector:sel2
withObject:@"138383438"
withObject:@"abcdefg"];

       

   
  3 配合对象将SEL类型作为方法的形参

        Car *c = [Car
new];

        SEL sel =
@selector(run);    

        Person *p = [Person new];

        [p makeObject:c
andSel:sel];
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Objective-C OC