一: 类的合理设计
//定义枚举
typedef enum {
SexMan,
SexWoman
}Sex;
//定义结构体
typedef struct{
int year;
int moonth;
int day;
} Date;
二:成员变量的命名规范
一定要以下划线_开头
作用:
1>让成员变量个get方法的名称区分开
2>可以跟局部变量区分开,一看到下划线开头的变量,一般都是成员变量
三:iOS的方法只有类方法和对象方法 (可以允许类方法和对象方法同名 虽然同名但是类方法是加好开头对象方法是减号开头)
对象方法:
1>减号-开头
2>只能由对象来调用
3>对象方法中能访问当前对象的成员变量(实例变量)
类方法
1>加号+开头
2>只能由类(名)调用
3>类方法中不能访问成员变量(实例变量)
类方法的好处和使用场合
1>不依赖于对象,执行效率高
四:self的用途
1>self指向了当前对象(方法调用者)
谁调用了当前方法,self就代表谁
self出现在对象方法中,self就代表对象
self出现在类方法中,self就代表类
2>可以利用self->成员变量名 访问当前对象内部的成员变量
3>[self 方法名]可以调用其他对象方法\类方法
五:封装
工具类(封装类方法):特点基本没有任何成员变量,里面的方法基本都是类方法
六:继承和组合
继承:
子类继承父类,相当于拥有了父类里面的多有成员变量和方法
不允许子类和父类拥有相同名称的成员变量
允许子类拥有和父类一样的方法(相当于重写了父类的方法)
6.1 重写:
子类重新实现父类中的某个方法, 覆盖父类以前的做法
注意:
1>父类必须声明在子类的前面
2>子类不能拥有和父类相同的成员变量
3>调用某个方法时,优先去当前类中找,如果找不到就去父类中找
6.2组合
继承和组合的选择
继承的使用场合:
1>当两个类拥有相同属性和方法的时候,就可以将相同的东西抽取到一个父类中
2>当A类完全拥有B类中的部分属性和方法时,就可以考虑让B类继承A类
继承是 xx是xxx 举例 狗是动物
组合是xxx拥有xxx 举例 人拥有狗
七:多态
多态就是父类指针指向子类对象
举例: 假设动物Amimal是父类 dog是继承与Amimal的子类 Amimal *animal = [dog new]; [animal eat];但是在调用方法的时候,系统会动态监测对象的真实形象 animal这个对象其实是一个狗对象
八:点语法
oc的点语法其实就是调用的get或者set方法(点语法 的本质还是方法调用)
8.1 死循环注意
举例 写一个年龄age的set和get方法中引起的死循环
- (void)setAge:(int)age{
_age = age;//这是可以的
self.age = age;//这就会引起死循环 相当于 [self setAge:age];
}
- (int)age{
return _age;//这是可以的
return self->age;//这也是可以的
return self.age; //这就会引起死循环 相当于 [ self age];
}
九:成员变量的作用域
9.1 类型
1>@private 只能在当前类的实现@implementation中直接访问
2>@protect 可以在当前类以及子类实现@implementation中直接访问 (默认就是@protect)
3>@public 任何地方都可以访问对象的成员变量
4>package 同一个"体系内"(框架)可以访问介于@private和 @public 之间
9.2
@implementation中定义的成员变量默认都是私有的,只能在当前类的实现中使用,但不能定义和@interface中同名的成员变量
@implementation中定义的成员变量默认都是私有的,通过添加@public 可以被外界访问,但是最好不要这样写
十:@property
10.1 @property (IOS 4.0之前)
作用:可以自动生成某个成员变量的setter和getter方法的声明
@synthesize (IOS 4.0之前) @synthesize age = _age;
作用:可以自动生成某个成员变量的setter和getter方法的实现,并且会访问下划线_age这个成员变量,如果成员变量不存在就会自动生成一个私有的@private成员变量age (此处age是为了举例说明)
如果手动实现了setter方法,则编译器只会自动生成getter方法
如果手动实现了getter方法,则编译器只会自动生成setter方法
10.2@property (IOS 4.0之后)
@property 就独揽了@synthesize 的功能,也就是说@property 可以同时生成getter和setter的声明和实现,默认情况下,getter和setter方法中的实现会去访问下划线_开头的成员变量
十一:id
简介:id是万能指针,指向任何oc对象,相当于NSObject *
使用: id p = [Person new]; //注意id后面不能加*
连接地址:https://www.jianshu.com/p/19f9e73f7d8f
id:是一种数据类型;
id类型被定义为指向对象的指针,这可以从id的定义中看出。id在objc.h中的定义为:
typedef struct objc_object {
Class isa;
} *id;
id是一个一个比较灵活的对象指针,并且是一个指向任何一个继承了Object(或者NSObject)类的对象。而在cocoa的开发环境里,NSObject是所有类的根类。所以id可以指向任何一个cocoa的合法对象。
Objective-C中的id这种数据类型存在的价值是什么?
id是一种通用的对象类型,她可以用类存储属于任何类的对象,可以理解为万能指针
***在id的定义中,已经包装好了*号,id指针只能指向os的对象
NSObject 和id都可以指向任何对象
NSObject对象会进行编译时检查(需要强制类型转换)
id不需要强制类型转换,id可以直接使用
编译器看到id以后,认为是动态类型,不在检查类型
id类型:
id是一个数据类型, 并且是一个动态数据类型
既然是数据类型, 所以就可以用来
1、定义变量
2、作为函数的参数
3、作为函数的返回值
4、id==NSObject* 万能指针
id和NSObject *的区别:
NSObject*是一个静态数据类型id是一个动态数据类型默认情况下所有的数据类型都是静态数据类型
静态数据类型的特点:
1、在编译时就知道变量的类型,
2、知道变量中有哪些属性和方法
3、在编译的时候就可以访问这些属性和方法,
4、如果是通过静态数据类型定义变量, 如果访问不了属于静态数据类型的属性和方法, 那么编译器就会报错
动态数据类型的特点:
1、在编译的时候编译器并不知道变量的真实类型, 只有在运行的时候才知道它的真实类型
2、如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错
通过静态数据类型定义变量, 不能调用子类特有的方法
通过动态数据类型定义变量, 可以调用子类特有的方法
通过动态数据类型定义的变量, 可以调用私有方法
弊端: 由于动态数据类型可以调用任意方法, 所以有可能调用到不属于自己的方法, 而编译时又不会报错, 所以可能导致运行时的错误
应用场景: 多态, 可以减少代码量, 避免调用子类特有的方法需要强制类型转换
为了避免动态数据类型引发的运行时的错误, 一般情况下如果使用动态数据类型定义一个变量, 在调用这个对象的方法之前会进行一次判断, 判断当前对象是否能够调用这个方法
instancetype和id的区别:
1、instancetype==id== 万能指针 == 指向一个对象
2、id在编译的时候不能判断对象的真实类型
3、instancetype在编译的时候可以判断对象的真实类型(一个在编译时不知道真实类型, 一个在编译时知道真实类型)
4、id可以用来定义变量, 可以作为返回值, 可以作为形参
5、instancetype只能用于作为返回值,它会进行类型检查,如果创建出来的对象,赋值了不相干的对象就会有一个警告信息,防止出错
十二:构造方法
简介:用来初始化对象的方法
在初始化的方法中一定要调用父类的[super init]方法(父类的属性交给父类的方法处理,子类方法处理子类自己的属性)如何完整的创建一个可用的对象 1:分配合理的存储空间 +alloc 2:调用-init进行初始化
连接 https://www.jianshu.com/p/9d38caa5c98a
构造方法
什么是构造方法:初始化对象的方法。
一般情况下,在 OC 当中创建1个对象分为两部分(new 做的事):
alloc:分配内存空间,init :初始化对象。
构造方法分为系统自带和自定义构造方法。
(1)如果是系统自带的构造方法,需要重写父类中自带的构造方法 比如init
(2)如果是自定义构造方法:属于对象方法那么以-号开头,返回值一般为id或者instancetype类型,方法名一般以init开头
系统构造方法书写格式:
- (instancetype)init{
self= [superinit];
if(self) {
}
returnself;
}
自定义构造方法:
@property int age;
@property NSString *name;//自定义构造方法 在初始化的时候为属性"年龄"和"姓名"赋值
- (instancetype)initWithAge:(int)age andName:(NSString*)name;
实现
// 实现自定义构造函数 在初始化的时候为属性赋值
- (instancetype)initWithAge:(int)age andName:(NSString*)name{
if(self= [superinit]) {
_age = age;
_name = name;
}
return self;
}
十三:分类
分类的作用:在不改变原来类内容的基础上,可以为分类增加一些方法
分类的使用注意:
1>分类只能增加方法,不能增加成员变量
2>分类方法实现中可以访问原来类中声明的成员变量
3>调用方法的时候会优先去分类中查找,然后再去原来类中查找,最后再去父类中查找
方法调用的优先级 分类(最后参与编译的分类优先)>>原来类>>父类
4>分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没办法在使用(因为方法的调用顺序有优先级)
十四:类
类本身也是一个对象(万物皆对象)是一个class类型的对象,简称类对象
类对象 == 类
创建类对象的两种方式 假设有个person类
//第一种方法 利用class创建类对象
class c = [person class];
///第二种方法
person *p1 =[ [person alloc] init];
class c = [p1 class];
十五:+(void)load 方法 和+(void)initialize方法
+(void)load
在类被加载的时候调用,也就是程序一启动这个方法就会被调用(不管程序运行过程中有没有用到这个类,都会调用+ load方法)
+(void)initialize
当第一次使用这个类的时候,就会调用一次+ initialize,并且只会调用一次这个方法
总结:
1>当程序启动的时候就会加载项目中的类和分类,而且加载后会调用"每个类和分类"的 + load 方法,并且只会调用一次(每一个都会调用)
2>当第一次使用某个类的时候,就会调用当前的+ initialize方法和父类的+ initialize(当前的和父类的,也就是说子类的不会调用)
3>先加载父类,在加载子类(先调用父类的+ load 方法,在调用子类的+ load 方法,先加载原始类,在加载分类)
先初始化父类,再初始化子类(先调用父类的+ initialize方法再调用子类的+ initialize方法)
十六: SEL
1>方法的存储位置
1.1 每个类的方法列表都存储在类对象中
1.2每个方法都有一个与之对应的SEL类型的对象
1.3根据一个SEL对象就可以找到方法的地址,进而调用方法
1.4 SEL类型的定义
typedef struct objc_selectore *SEL
2>SEL对象的创建
第一种:SEL s = @selector(方法名);
第二种 SEL s = NSSelectorFromString(@"test");
3>举例
3.1举例 -----1
Person *p = [[Person alloc]init];
[p test];//假设Person 类中有一个test方法
对象p调用test方法相当于(程序内部执行的实际为)
[p performSelector:@selectore(test)];
解释说明:
1->把test包装成SEL类型的数据
2->根据SEL数据找到对应的方法地址
3->根据方法地址调用对应的方法
3.2举例 ------ 2
Person *p = [[Person alloc]init];
[p test:@"123"];//假设Person 类中有一个test:方法
对象p调用test方法相当于(程序内部执行的实际为)
[p performSelector:@selectore(test:) withObject:@"123"];
网友评论