几天前忽然在别人的类的.m文件中看到这么一句代码:@synthesize xxxx = _xxxx; 当时愣是没理解啥意思,事后才缓过神来发现原来是把一些类的基础知识忘记了,虽然不用过多去深究之前的一些旧东西,但可是既然遇到了,仍是复习一下。json
1.类:类是定义同一类全部属性和方法的蓝图或原型。设计模式
2.对象:用来描述客观事物的一个实体,由具体的属性和方法构成。多线程
3.类与对象的关系:类是用来制做无数实体(对象)的工程图纸。架构
4.类的特征:属性app
5.类的行为:方法框架
1.类就是封装,封装了属性与方法。它是一种思想,其核心就是“暴露出必要的内容给外部用(属性方法私有),而对于内部细节,使用者不用去关心”。函数
咱们声明一个Teacher类:学习
#import <Foundation/Foundation.h>
@interface Teacher : NSObject {
// @public 公共的 NSString *_name; //成员变量(实例变量) 默认@protected修饰(受保护的)
/* 顺带一提:@protected、@public、@private、@package
* @public 成员变量能够被在任何地方访问。
* @protected 成员变量能被声明它的类和子类访问(默认)
* @private 成员变量只能在声明它的类中访问(默认如今用@Property关键字生成的成员变量是这个)
* @package 一个@package成员变量在实现这个类的可执行文件镜像中其实是@public的,可是在外面就是@private。
*/ }@end
然而,咱们此时在外面根本没法访问到这个受保护的成员变量。ui
访问该成员变量的方法:atom
1> 打开上面 “@public”的注释,在外面以"->“方式访问
Teacher *tec = [[Teacher alloc] init]; tec->_name = @"姓名";
2> 封装set、get方法进行间接访问,在.h文件中加上方法声明,在.m文件中实现
-(void)setName:(NSString *)name; -(NSString *)getName; -(void)setName:(NSString *)name { _name = name; } -(NSString *)getName { return _name; }
这样,即可以在外面经过调用方法的方式来间接访问该成员变量,其实下面讲的@property关键字就是帮你解决了这个set、get方法的访问
让其在外面以"."的方式(本质上是调用set和get方法)间接访问。
Teacher *tec = [[Teacher alloc] init]; [tec setName:@"testName"]; NSLog(@"%@",[tec getName]);
总结一下成员变量和成员属性:
①成员变量即实例变量
②成员属性用于间接访问类中的成员变量
③假如你想让类的一个特性私有,只在本类访问,就定义成成员变量;假如你想在类以外访问该类的一个特性,你就将其定义成成员属性。
1.在之前,咱们常常能够看到这种类的声明
#import <Foundation/Foundation.h>
@interface Teacher : NSObject { NSString *_name; //成员变量 } @property (nonatomic,copy)NSString *name; //成员属性 @end
#import "Teacher.h"
@implementation Teacher @synthesize name = _name; @end
这是在以前的编译器特性中:
@property帮咱们声明了name属性的set和get方法
@synthesize name = _name则表示对@property声明的属性name实现set和get方法,后面的” = _name“表示实现的时候访问成员变量_name
2.而如今咱们为类声明一个属性则一般只须要一句话(我通常都用这种,方便):
@property (nonatomic,copy)NSString *name; //这里指在.h文件中声明
这是在新的编译器特性中:
@property直接帮助咱们干了三件事:
①当该name属性的成员变量未被指定时,会生成一个默认为_name的成员变量,可是该属性为@private(私有的),本身写的则是@protected(受保护的)
②声明name属性的set、get方法
③实现name属性的set、get方法
注:若是你想要用本身生成的成员变量,则用以前的那种格式,在.h文件中增长一个成员变量,在.m文件中加上@synasize xxx = _xxx。
成员变量用”_“下划线开头命名的好处:
①区分红员变量和属性
②防止局部变量和成员变量命名冲突
1.继承:创建类之间的关系,实现代码的重用性,方便系统扩展。
2.继承是为了不多个类似的类中相同的成员变量反复定义而延伸的一个特性。
3.继承有单根性与传递性
4.被继承的类称之为父类或基类,继承的类称之为子类或派生类
5.super关键字:self用于访问本类成员,而super则是用于在子类中访问父类成员
分类:①在不改变原来类的基础上,为类增长一些方法,来对类进行一个扩展。
②在开发中,咱们通常对都是为系统提供的类添加分类。还有就是将本身的类分模块,将实现不一样功能的方法写在不一样的分类中,一个类能够有无限个分类。
1.分类又称为非正式协议:NSObject类及其子类的一个类别(catagory)。
#import "Teacher.h"
@interface Teacher (Log) +(void)log; @end
#import "Teacher+Log.h"
@implementation Teacher (Log) +(void)log
{
NSLog(@"分类");
} @end
如上面所写的分类,咱们为Teacher类添加了一个分类"Teacher+Log.h",分类中声明并实现了类方法+(void)log;
2.类的延展:匿名分类。
在Teacher.m文件中
#import "Teacher.h"
@interface Teacher () //这种写法就是匿名分类 @property (nonatomic,copy)NSString *nickName; //匿名分类中能够添加属性,可是该属性默认为@private,只能在本类中使用 +(void)log; //声明了一个类方法log @end
@implementation Teacher @synthesize name = _name; //实现该类方法 +(void)log { NSLog(@"匿名分类"); } @end
接着咱们调用log方法,出现以下打印
这里证实:分类(非正式协议)会重写本类及其类扩展(匿名分类)的方法。
3.catagory中匿名分类容许添加属性并会自动生成成员变量和set、get方法;可是在非正式协议中,容许你用@property声明一个属性,不会为你提供set、get方法以及成员变量,若是你直接使用的话,形成崩溃。以下
1> 我为Teacher + MyProperty.h分类头文件中增长了一个属性nickName;
#import "Teacher.h"
@interface Teacher (MyProperty) @property (nonatomic,copy)NSString *nickName; @end
2>使用(缺乏set、get方法致使崩溃)
Teacher *tec = [[Teacher alloc] init]; tec.nickName = @"nickNmae"; NSLog(@"%@",tec.nickName); // 打印
2016-01-24 16:58:13.656 分类[2002:159549] -[Teacher setNickName:]: unrecognized selector sent to instance 0x100213e10
2016-01-24 16:58:13.658 分类[2002:159549] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Teacher setNickName:]: unrecognized selector sent to instance 0x100213e10'
*** First throw call stack:……
3>如何在非正式协议(分类)中添加属性【oc针对这一现象,提供了一个解决方案:关联对象(Associated Object)】,在分类的.m文件中写上以下代码
#import "Teacher+MyProperty.h"
#import <objc/runtime.h>
static void *strKey = &strKey; @implementation Teacher (MyProperty) -(void)setNickName:(NSString *)nickName { objc_setAssociatedObject(self, &strKey, nickName, OBJC_ASSOCIATION_COPY); } -(NSString *)nickName { return objc_getAssociatedObject(self, &strKey); } @end
1.有非正式协议,天然也有正式协议,protocol即是,用于声明一大堆方法,等待实现,只要某个类遵照了某个协议,那么这个类就拥有了该协议的全部方法声明。(注意:协议只存在.h声明文件)其格式以下:
@protocol Teach <NSObject>
@required;
-(void)teach:(NSString *)text; // 声明了一个teach的方法,而且为必须实现,默认是@optional; @end
//新建了一个名为Teach.h的协议头文件
2.类能够遵照协议(Teacher遵照teach协议)
3.实现方法
-(void)teach:(NSString *)text { NSLog(@"老师教书"); }
4.一个类只能继承自一个父类(继承的单根性),可是一个类能够遵照多份协议.
1.关联对象相似于成员变量,可是它是在运行时被添加的。(用于解决分类中添加属性的问题,上面已经介绍了缘由,这里不作介绍)
2.咱们能够把关联对象想象成一个OC对象,这个对象经过key链接到一个类的实例变量上。
3.因为使用的是C接口,因此key是一个void指针(const void *)。咱们还须要指定一个内存管理策略,以告诉Runtime如何管理这个对象的内存。内存管理策略选项值以下:
// 当宿主对象被释放时,会根据指定的内存管理策略来处理关联对象。当咱们须要在多个线程中处理访问关联对象的多线程代码时,这就很是有用了
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, OBJC_ASSOCIATION_COPY_NONATOMIC = 3, OBJC_ASSOCIATION_RETAIN = 01401, OBJC_ASSOCIATION_COPY = 01403 }; /* 若是指定的策略是assign,则宿主释放时,关联对象不会被释放; 而若是指定的是retain或者是copy,则宿主释放时,关联对象会被释放。 咱们甚至能够选择是不是自动retain/copy。 */
4.使用
1> void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)方法:将一个对象链接到其它对象
参数1:源对象,通常传self.
参数2:key,用来表示是哪一属性的key,可能在分类中添加不止一个属性。
常见有三种写法:
① static void *strKey = &strKey;
②
static NSString *strKey = @"strKey";
③ static char strKey;
参数3:关联的对象
参数4:关联策略
self对象将获取一个新的关联的对象anObject,且内存管理策略是自动retain关联对象,当self对象释放时,会自动release关联对象。另外,若是咱们使用同一个key来关联另一个对象时,也会自动释放以前关联的对象,这种状况下,先前的关联对象会被妥善地处理掉,而且新的对象会使用它的内存。
2> id anObject = objc_getAssociatedObject(self, &myKey)方法 ,获取关联的对象
3> objc_removeAssociatedObjects(self)方法,移除全部关联
五、应用(UIAlertView + Block分类,以自身为delegate监控选项按钮点击事件,以block做为关联对象)
#import <UIKit/UIKit.h> typedef void(^CompleteBlock) (NSInteger buttonIndex); @interface UIAlertView (Block) // 用Block的方式回调,这时候会默认用self做为Delegate
- (void)showAlertViewWithCompleteBlock:(CompleteBlock) block; @end
#import "UIAlertView+Block.h"
#import <objc/runtime.h>
@implementation UIAlertView (Block) static char key; // 用Block的方式回调,这时候会默认用self做为Delegate
- (void)showAlertViewWithCompleteBlock:(CompleteBlock)block { if (block) { //移除全部关联
objc_removeAssociatedObjects(self); /** 1 建立关联(源对象,关键字,关联的对象和一个关联策略。) 2 关键字是一个void类型的指针。每个关联的关键字必须是惟一的。一般都是会采用静态变量来做为关键字。 3 关联策略代表了相关的对象是经过赋值,保留引用仍是复制的方式进行关联的;关联是原子的仍是非原子的。这里的关联策略和声明属性时的很相似。 */ objc_setAssociatedObject(self, &key, block, OBJC_ASSOCIATION_COPY); //设置delegate
self.delegate = self; }
[self show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { ///获取关联的对象,经过关键字。
CompleteBlock block = objc_getAssociatedObject(self, &key); if (block) { ///block传值
block(buttonIndex); } } /** OC中的关联就是在已有类的基础上添加对象参数。来扩展原有的类,须要引入#import <objc/runtime.h>头文件。关联是基于一个key来区分不一样的关联。 经常使用函数:
objc_setAssociatedObject 设置关联 objc_getAssociatedObject 获取关联 objc_removeAssociatedObjects 移除关联 */
//固然也能够没必要这么麻烦,使用继承一样能够轻易作到。
@end
/** * 这两个方法是在程序运行一开始就被调用的方法. * 咱们能够利用他们在类被使用前,作一些预处理工做. * 好比我碰到的就是让类自动将自身类名保存到一个NSDictionary中. */
//而initialize是在类或者其子类的第一个方法被调用前调用。
+(void)initialize; //load方法是只要类所在文件被引用就会被调用.
+(void)load; /** * 因此若是类没有被引用进项目,就不会有load调用; * 但即便类文件被引用进来,可是没有使用,那么initialize也不会被调用 */
1.反射:简而言之就是经过类名返回类的对象(使用前导入#import <objc/runtime.h>头文件,今天前面的关联对象也用到这个头文件,好像关于这个runtime要学的东西有点多,复习着一会儿扒出了好多要学的新东西,- _ -,先从基础的慢慢来吧)
// 类名返回类对象
+(NSObject *)createBean:(NSString *)className { Class tempClass = NSClassFromString(className); NSObject *obj; if (tempClass) { obj = [[tempClass alloc]init]; } return obj; }
2.类名获得属性名集合
+(NSArray *)propertyOfClass:(NSString *)className { NSMutableArray *arr = [NSMutableArray arrayWithCapacity:0]; //经过类名得到类的属性
const char *cClassName = [className UTF8String]; id theClass = objc_getClass(cClassName); unsigned int outCount, i; objc_property_t *properties = class_copyPropertyList(theClass, &outCount); for (i = 0; i < outCount; i++) { objc_property_t property = properties[i]; NSString *propertyNameString = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; [arr addObject:propertyNameString]; } return arr; }
3.经过反射(结合KVC),经过类名、数据源、约定,咱们就能够实现json字符串转对象。
4.具体以后三方库学习MJExtension中NSObject + MJClass转模型。
1.类簇是Foundation框架中普遍使用的设计模式(抽象工厂模式)。类簇将一些私有的、具体的子类组合在一个公共的、抽象的超类下面,以这种方法来组织类能够简化一个面向对象框架的公开架构,而又不减小功能的丰富性。
2.类簇的概念(选自百度百科。。。)
类簇 是一群隐藏在通用接口下的与实现相关的类,使得咱们编写的代码能够独立于底层实现(由于接口是稳定的)。
如建立NSString对象时,你获得的多是NSLiteralString,NSCFString,NSSimpleCString等。即不一样的NSString对象调用同一个接口A,接口A的实现多是不一样的。
在Foundation框架中,常见的类簇有NSNumber,NSString,NSArray,NSDictionary等。 想要在类簇中建立子类会困难一些,必须是抽象超类的子类,必须重载超类的原始方法,必须声明本身的数据存储。最方便的是使用组合或者类别来代替子类化。
3.关于重写init方法时候调用父类的init方法:self = [super init]和[super init]
咋看之下,产生了一个疑问,为何[super init]要赋值给self。今天看了下类簇的概念,终于明白了,因为在有些状况下,init可能会改变返回的对象,这时候必须赋值给self。
四、不推荐继承类簇
当你继承某个类簇的超类以后,子类可使用父类的方法,结果你去调用该类簇的超类的方法,你会发现程序崩溃了,缘由是因为在超类的方法的实现里,你不知道它实际调用的它里面的哪个子类的方法(这些组合的子类声明和实现都隐藏在该超类的.m文件里面),结果你的类是直接继承自该类簇的超类,也就是说你的类根本就不认识这个方法。