iOS单例模式的2种方式。根据线程安全的实现来区分,一种是使用@synchronized ,另一种是使用GCD的dispatch_once函数。
要实现单例,首先需要一个static的指向类本身的对象,其次需要一个初始化类函数。下面是两种实现的代码。
@synchronized
static InstanceClass *instance; + (InstanceClass *)shareInstance{ @synchronized (self){ if (instance == nil) { instance = [[InstanceClass alloc] init]; } } return instance; }
GCD
static InstanceClass *instance; + (InstanceClass *)shareInstance{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[InstanceClass alloc] init]; }); return instance; }
示例:
+(MyClass *)shareInstance { static dispatch_once_t once; static id instance; dispatch_once(&once, ^{ instance = [[self alloc]init]; }); return instance; }
Notification(通知)
//向通知中心添加消息监听名称,和通知方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDisappear:) name:@"DismissFaceVerifyController" object:nil]; //通过通知中心调用对应消息名 [[NSNotificationCenter defaultCenter] postNotificationName:@"DismissFaceVerifyController" object:self userInfo:nil];
KVO(键值监听),即Key-Value Observing
监听对象的属性(成员变量)变化,对象可以是自己也可以是其它
//实例监听自己的属性
//ViewController.m文件
// // ViewController.m // KVOLearn // // Created by Vie on 2017/4/6. // Copyright © 2017年 Vie. All rights reserved. // #import "ViewController.h" @interface ViewController () @property(strong, nonatomic) NSString *testStr; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.testStr=@"Tracer"; //按钮 UIButton *loginBtn=[[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-50, 350, 100, 50)]; [loginBtn setTitle:@"改变值" forState:UIControlStateNormal]; [loginBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [loginBtn setBackgroundColor:[UIColor redColor]]; [loginBtn.layer setBorderWidth:0.2f]; [loginBtn.layer setCornerRadius:10.0f]; [loginBtn addTarget:self action:@selector(changeAction:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:loginBtn]; /*1.注册对象self为被观察者: option中, forKeyPath为被观察的对象属性 NSKeyValueObservingOptionOld 以字典的形式提供 “初始对象数据”; NSKeyValueObservingOptionNew 以字典的形式提供 “更新后新的数据”; */ [self addObserver:self forKeyPath:@"testStr" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil]; } //按钮事件 -(void)changeAction:(UIButton *)sender{ self.testStr=@"Vie"; } /* 只要object的keyPath属性发生变化,就会调用此回调方法*/ -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ if ([keyPath isEqualToString:@"testStr"]) { NSLog(@"监听到testStr属性值改变"); } } -(void)dealloc{ //移除self中forKeyPath属性观察 [self removeObserver:self forKeyPath:@"testStr" context:nil]; } @end
delegate
委托应用场景:一个view把一些业务交给 控制器去实现,然后控制器实现委托改变view
//在ViewController.h中声明协议如下
@protocol ByValueDelegate <NSObject> -(void) passValue:(NSString *) value; @end
//在ViewController.h中声明委托变量
@property (nonatomic,weak) id<ByValueDelegate> delegate;//声明一个委托变量
//在ViewController.m中设置代理
UserinfoView *uiv=[[UserinfoView alloc]init]; //把值委托到UserinfoView.m的实现委托的方法里面 self.delegate=uiv;//设置该对象代理为对象uiv [self.delegate passValue:@"委托传值跳转"];
//在UserinfoView.h中,引用ViewController的头文件,并添加代理协议如下
#import "ViewController.h" @interface UserinfoView : UIViewController<ByValueDelegate>@property (nonatomic,strong) UILabel *showLable; @property (nonatomic,retain) UserinfoEntity *showUserinfoEntity; -(void) viewDidLoad;@end
//在UserinfoView.m中实现代理函数
//实现委托方法 -(void) passValue:(NSString *)value{ UIAlertView *alterOk = [[UIAlertView alloc] initWithTitle:@"提示" message:value delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alterOk show]; }
KVC(键值编码),即Key-Value Coding,一个非正式的Protocol,使用字符串(键)访问一个对象实例变量的机制
就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。
KVC在iOS中的定义 无论是Swift还是Objective-C,KVC的定义都是对NSObject的扩展来实现的(Objective-c中有个显式的NSKeyValueCoding类别名,而Swift没有,也不需要)所以对于所有继承了NSObject在类型,都能使用KVC(一些纯Swift类和结构体是不支持KVC的),下面是KVC最为重要的四个方法
//KVC最为重要的四个方法 - (nullable id)valueForKey:(NSString *)key; //直接通过Key来取值 - (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值 - (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来取值 - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值
//例
//PersonInfoModel.h文件
// // PersonInfoModel.h // KVCLearn // 个人信息 // Created by Vie on 2017/4/6. // Copyright © 2017年 Vie. All rights reserved. // #import <Foundation/Foundation.h> @interface PersonInfoModel : NSObject @property(strong, nonatomic) NSString *name;//姓名 @property(strong, nonatomic) NSString *gender;//性别 @property(assign, nonatomic) NSNumber *age;//年龄 @end
//StudentInfoModel.h文件
// // StudentInfoModel.h // KVCLearn // 学生信息 // Created by Vie on 2017/4/6. // Copyright © 2017年 Vie. All rights reserved. // #import <Foundation/Foundation.h> #import "PersonInfoModel.h" @interface StudentInfoModel : NSObject @property(strong, nonatomic)PersonInfoModel *personModel;//个人信息 @property(strong, nonatomic)NSString *studentID;//学号 @property(strong, nonatomic)NSString *departments;//院系 @end
//使用
#import "StudentInfoModel.h" StudentInfoModel *studentModel=[[StudentInfoModel alloc] init]; //设值 [studentModel setValue:@"信息技术学院" forKey:@"departments"]; [studentModel setValue:@"Vie" forKeyPath:@"personModel.name"]; //取值 NSLog(@"%@",[studentModel valueForKey:@"departments"]); NSLog(@"%@",[studentModel valueForKeyPath:@"departments"]);
修改系统控件内部属性(runtime + KVC)
将UIPageControl样式原来样式
修改为下面
PageControl公开属性没有直接可以修改的,目前可以自定义PageControl
,这种方式看起来不简单。另一种方式就是,通过runtime遍历出UIPageControl
所有属性(包括私有成员属性,runtime确实很强大)。
//遍历属性 unsigned int count = 0; Ivar *ivars = class_copyIvarList([UIPageControl class], &count); for (int i = 0; i < count; i++) { Ivar ivar = ivars[i]; //获取所有私有属性 const char *property = ivar_getName(ivar); NSLog(@"%@",[[NSString alloc]initWithCString:property encoding:NSUTF8StringEncoding]); }
//运行结果
2017-04-06 20:12:20.756 KVCLearn[4430:220644] _lastUserInterfaceIdiom 2017-04-06 20:12:20.756 KVCLearn[4430:220644] _indicators 2017-04-06 20:12:20.757 KVCLearn[4430:220644] _currentPage 2017-04-06 20:12:20.757 KVCLearn[4430:220644] _displayedPage 2017-04-06 20:12:20.757 KVCLearn[4430:220644] _pageControlFlags 2017-04-06 20:12:20.757 KVCLearn[4430:220644] _currentPageImage 2017-04-06 20:12:20.757 KVCLearn[4430:220644] _pageImage 2017-04-06 20:12:20.757 KVCLearn[4430:220644] _currentPageImages 2017-04-06 20:12:20.758 KVCLearn[4430:220644] _pageImages 2017-04-06 20:12:20.758 KVCLearn[4430:220644] _backgroundVisualEffectView 2017-04-06 20:12:20.758 KVCLearn[4430:220644]_currentPageIndicatorTintColor 2017-04-06 20:12:20.758 KVCLearn[4430:220644] _pageIndicatorTintColor 2017-04-06 20:12:20.758 KVCLearn[4430:220644] _legibilitySettings 2017-04-06 20:12:20.758 KVCLearn[4430:220644] _numberOfPages
//找到了属性后,接着实现
//然后通过KVC设置自定义图片,实现了效果,代码如下: UIPageControl *pageControl = [[UIPageControl alloc] init]; [pageControl setValue:[UIImage imageNamed:@"home_slipt_nor"] forKeyPath:@"_pageImage"]; [pageControl setValue:[UIImage imageNamed:@"home_slipt_pre"] forKeyPath:@"_currentPageImage"];
block中使用修改方法外的属性需要用__block修饰,例如:
__block BOOL whoopsSomethingWrongHappened = true;
#import "ViewController.h" #import "CallbackTest.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self.view setBackgroundColor:[UIColor whiteColor]]; UIButton *backBtn=[[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width*0.4, self.view.frame.size.height*0.475, self.view.frame.size.width*0.2, self.view.frame.size.height*0.05)]; [backBtn.layer setCornerRadius:5.0]; [backBtn setTitle:@"回调" forState:UIControlStateNormal]; [backBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [backBtn.layer setBorderWidth:0.5]; [backBtn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:backBtn]; } /** * @Author Vie, 2015-08-05 13:41:37 * 使用block回调函数 * @param sender sender description * * @since <#version number#> */ -(void)btnAction:(UIButton *)sender{ CallbackTest *backTest=[[CallbackTest alloc] init]; //第一种回调方式 // [backTest startString:@"回调开始" callBack:^(NSString *backFunstring) { // 回调时候会执行 // UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"回调提示" message:backFunstring delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; // [alert show]; // // }]; //第二种回调方式 backTest.backCall=^(NSString *string){ UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"回调提示"message:string delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alert show]; }; [backTest blockCall:@"回调开始"]; } @end
#import <UIKit/UIKit.h> @interface CallbackTest : NSObject //定义一个block,返回值为void,参数为NSString typedef void(^backFunction)(NSString *backFunstring); @property (nonatomic,copy) backFunction backCall; -(void)startString:(NSString *)str callBack:(backFunction)backString; -(void)blockCall:(NSString *)str;//第一一个block属性 @end
#import "CallbackTest.h" @implementation CallbackTest //第一种回调方式 -(void)startString:(NSString *)str callBack:(backFunction)backString{ NSMutableString *mutString=[str mutableCopy]; [mutString appendString:@"回调完成"]; //执行block // backString(mtuString); //将block回调交给另一个方法执行 [self TestBackCall:mutString callBack:backString]; } //执行block回调的函数 -(void)TestBackCall:(NSMutableString *)mutString callBack:(backFunction)testBack{ testBack(mutString); } //第二种回调方式 -(void)blockCall:(NSString *)str{ NSMutableString *mutString=[str mutableCopy]; [mutString appendString:@"回调完成"]; _backCall(mutString); } @end
//使用NSClassFromString,通过字符串获取类 Class class=NSClassFromString(@"UIAlertView"); //或者使用objc_getClass,通过字符串获取类 //Class class=objc_getClass("UIAlertView"); id alert= [[class alloc] initWithTitle:@"123" message:@"111" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alert show];
使用NSPointerArray进行多代理可以跟踪内存,它是mutable,数组不会增加这个对象的引用计数
//例
//MoreDelegate.h文件
// // MoreDelegate.h // MoreDelegate // 委托 // Created by Vie on 2017/5/2. // Copyright © 2017年 Vie. All rights reserved. // @protocol MoreDelegate <NSObject> @optional -(void)testMoreDelegate;//多代理测试 @end
//后面可以结合通知(或者其他方式)使用,到达方法准确调用带参。
//MoreDelegateManager.h文件
// // MoreDelegateManager.h // MoreDelegate // 多代理管理实现类 // Created by Vie on 2017/5/2. // Copyright © 2017年 Vie. All rights reserved. // #import <Foundation/Foundation.h> #import "MoreDelegate.h" @interface MoreDelegateManager : NSObject /** 单例模式 @return self */ +(MoreDelegateManager *)shareInstance; /** 注册代理 @param obj 需要实现代理的对象 */ -(void)registerDelegate:(id)obj; /** 解除代理 @param obj 需要接触代理的对象 */ -(void)unRegisterDelegate:(id)obj; /** 执行代理方法(后续可以多个方法提供出去,方便传参调用) @param aSelector 类方法选择器 */ -(void)delegateAction:(SEL)aSelector; @end
//MoreDelegateManager.m文件
// // MoreDelegateManager.m // MoreDelegate // // Created by Vie on 2017/5/2. // Copyright © 2017年 Vie. All rights reserved. // #import "MoreDelegateManager.h" #import <objc/runtime.h> #import <objc/message.h> @interface MoreDelegateManager ()<MoreDelegate> @property(nonatomic,strong) NSPointerArray *delegateArr;//实现委托的数组 @end @implementation MoreDelegateManager #pragma mark 懒加载 -(NSPointerArray *)delegateArr{ if (!_delegateArr) { _delegateArr=[NSPointerArray weakObjectsPointerArray]; } return _delegateArr; } #pragma mark 对外方法 /** 单例模式 @return self */ +(MoreDelegateManager *)shareInstance{ static id instanc; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instanc=[[self alloc] init]; }); return instanc; } /** 注册代理 @param obj 需要实现代理的对象 */ -(void)registerDelegate:(id)obj{ NSLog(@"注册代理类:%@",NSStringFromClass([obj class].class)); [self.delegateArr addPointer:(__bridge void*)obj]; } /** 解除代理 @param obj 需要接触代理的对象 */ -(void)unRegisterDelegate:(id)obj{ NSLog(@"解除注册代理类:%@",NSStringFromClass([obj class].class)); NSUInteger index=[self indexOfDelegte:obj]; if (index!=NSNotFound) { [self.delegateArr removePointerAtIndex:index]; } //去掉数组里面的野指针 [self.delegateArr compact]; } /** 执行代理方法 @param aSelector 类方法选择器 */ -(void)delegateAction:(SEL)aSelector{ for (id delegate in self.delegateArr) { if (delegate&&[delegate respondsToSelector:aSelector]) { // ((void (*) (id, SEL)) objc_msgSend) (delegate, aSelector); [delegate testMoreDelegate]; } } } #pragma mark 内部使用方法 /** 计算Delegate的下标 @param obj 代理委托类 @return 下标 */ -(NSUInteger)indexOfDelegte:(id)obj{ for (NSUInteger i=0; i<self.delegateArr.count; i++) { if ([_delegateArr pointerAtIndex:i]==(__bridge void*)obj) { return i; } } return NSNotFound; } @end
//使用
//在几个不同的控制器中注册代理 [[MoreDelegateManager shareInstance] registerDelegate:self]; //调用事件 [[MoreDelegateManager shareInstance] delegateAction:@selector(testMoreDelegate)]; //实现委托方法 -(void)testMoreDelegate{ NSLog(@"代理实现类:%@",NSStringFromClass(self.class)); }