OC基础 04

OC 基础04

[email protected]基本概念

>[email protected]是编译器的指令

>[email protected] 用在声明文件中告诉编译器声明成员变量的的访问器(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 (readonly) int age;

指定所生成的方法的方法名称

getter=你定制的getter方法名称

setter=你定义的setter方法名称(注意setter方法必须要有 :)

@property (getter=isMarried)  BOOL  married;
说明,通常BOOL类型的属性的getter方法要以is开头


[email protected]

@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

使用注意:

(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];


类对象的存储


8.SEL

SEL类型代表着方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系

每个类的方法列表都存储在类对象中

每个方法都有一个与之对应的SEL类型的对象

根据一个SEL对象就可以找到方法的地址,进而调用方法

SEL类型的定义

typedef struct objc_selector *SEL;

首先把test这个方法名包装成sel类型的数据

根据SEL数据到该类的类对象中,去找对应的方法的代码,如果找到了就执行该代码

如果没有找到根据类对象上的父类的类对象指针,去父类的类对象中查找,如果找到了,则执行父类的代码

如果没有找到,一直像上找,直到基类(NSObject)

如果都没有找到就报错。

注意:

在这个操作过程中有缓存,第一次找的时候是一个一个的找,非常耗性能,之后再用到的时候就直接使用。

Dog *dog=[[Dog alloc] init];
[dog eat];

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方法查找顺序

1.给实例对象发送消息的过程(调用对象方法)

>根据对象的isA指针去该对象的类方法中查找,如果找到了就执行

>如果没有找到,就去该类的父类类对象中查找

>如果没有找到就一直往上找,直到跟类(NSObject)

>如果都没有找到就报错

2.给类对象发送消息(调用类方法)

>根据类对象的isA指针去元对象中查找,如果找到了就执行

>如果没有找到就去父元对象中查找

>如果如果没有找到就一直往上查找,直到根类(NSOject)

>如果都没有找到就报错

时间: 2024-08-10 21:04:31

OC基础 04的相关文章

四.OC基础--1.文档安装和方法重载,2.self和super&amp;static,3.继承和派生,4.实例变量修饰符 ,5.私有变量&amp;私有方法,6.description方法

四.OC基础--1.文档安装和方法重载, 1. 在线安装 xcode-> 系统偏好设置->DownLoads->Doucument->下载 2. 离线安装 百度xcode文档 3. 方法重载: 是指在一个类中定义多个同名的方法 在OC中没有重载 2.self和super&static, self和super: 1. self理解: 谁调用当前方法, self就代表谁. 比如: 在对象方法中,self代表的是对象, 因为只有对象才可以调用对象方法 在类方法中, self代表的

OC基础 可变字典与不可变字典的使用

OC基础 可变字典与不可变字典的使用 1.不可变字典 1.1创建不可变字典 //创建字典 //注意: //1,元素个数是偶数 //2,每两个元素是一个键值对 //3,值在前,键在后 NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:@"huang",@"name",@"30",@"age", nil]; NSLog(@"%@",

OC基础(23)

NSArray基本概念 NSArray 遍历 NSArray排序 NSArray文件读写 NSArray 与字符串 *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } a { color: #4183C4; } a.absent { color: #cc0000; } a.anchor { display: block; padding-left: 30p

黑马程序员--oc基础第六篇

六. oc基础知识(内存管理下) 总结:内存管理代码规范 1.只要调用alloc那么就必须调用release: 2.set方法的代码部分 *基本数据类型直接赋值. *如果是oc对象类型 - (void) setCar:(Car *)car { if(_car!=car) { [_car release]; _car=[Car retain]; } } 3.dealloc 部分的代码规范 *一定要[super dealloc ]放在最后面. *对self(当前)所拥有的其他对象做一次release

iOS开发OC基础:OC基础概念总结,OC面向对象的思想

一.什么是OOP: OOP(Object Oriented Programming):面向对象编程 二.面向对象和面向过程的区别: 面向过程编程:分析解决问题的步骤,实现函数,依次使用面向对象编程:分解问题组成的对象,协调对象间的联系和通信,解决问题. 面向过程是以事件为中心,关心的是完成这个事件的详细步骤:面向对象是以事物为中心,关心的是事物应该具备的功能,而完成一个事件只是事物所有功能里面的一个小功能(以过程为中心,以对象为中心) 三.类和对象 对象定义了解决问题的步骤中的行为,不刻意完成一

iOS开发OC基础:OC属性的一些概念和基本使用

一.属性简介 //属性是OC2.0之后新出的一种语法,能让我们快速的生成setter以及getter方法,大大简化代码 二.如何定义一个属性 //@property 属性关键字,用来定义属性 //NSString * 属性的类型 //name 属性的名字. //@property 只是生成的setter以及getter方法的声明.@property NSString *name; 三.属性的使用注意事项//如果在.m文件中我们自己实现了setter以及getter方法,则编译器就不会再帮我们生成

iOS开发OC基础:Xcode中常见英文总结,OC常见英文错误

在开发的过程中难免会遇到很多的错误,可是当看到系统给出的英文时,又不知道是什么意思.所以这篇文章总结了Xcode中常见的一些英文单词及词组,可以帮助初学的人快速了解给出的提示.多练习,就肯定能基本掌握. expression:表达式assignable:赋值variable:变量redefinition:重复定义type:类型conflicting:冲突项invalid:无效的conversion:转换specifier:说明符indent:缩进operands:运算对象.操作数binary:二

iOS开发OC基础:OC的内存管理

OC内存管理的基础知识 /** //             *  当对一个对象autorelease时,会将该对象放到离它最近的自动释放池,当自动释放池将要销毁时,会对自动释放池中的对象发送release消息,让对象的引用计数减1,(切记,是将对象的引用计数减1,而不是回收空间.) //             */ /** *  如果相对一个对象做copy操作,就必须让该类服从NSCopying协议,并且实现协议中的- (id)copyWithZone:(NSZone *)zone方法 */

iOS开发OC基础:OC中的分类(类目)

//分类,category,(类目) //为没有源代码的类添加方法 //一定要注意,只能添加方法,不能添加实例变量 /** *  分类 类的定义也是分为接口部分和实现部分 接口部分:以@interface开头 + 类名 后跟小括号,小括号内填写的是分类名 @end结束 在@interface 与@end 之间添加方法. */ //分类为原类添加的方法,就相当于原类具有该方法,可以正常调用 因为涉及到几个分类的创建,所以就直接上传代码了,其实代码也不多,只是怕大家在建立分类的时候会混淆,所以直接把