1.app启动main函数做了三步 创建UIApplication对象(可以是他的子类) APPDelegate代理(遵循UIApplicationDelegate的就可以) 创建runloop
2.要影藏状态栏可以采用window的windowLevel属性为UIWindowLevelAlert
3.APPDelegate里面可以用分类来写一些业务 友盟 环信 等等等 达到精简 剥离业务的作用
4.优化启动时间 +load少写 (load是在文件加载时候就执行的 main之前 可以用initialize代替)减少appdelegate里头的didfinishlaunch东西 或者放子线程里面执行
5.线程和runloop一一对应
6.控制器的view是loadView方法super继承自动出来的 也可以自己创建 如果什么都不写控制器的view为nil.
伪代码
- (UIView *)view {
if (!_view) {
[self loadView];
[self viewDidLoad];
}
return _view;
}
+(void)loadView {
//不继承了 MVCS就是利用loadView把控制器和view分开 把控制器的作用弱化 可以用kvo通信(封装一下kvo ) 控制器只做业务处理
CustomView *view = [[CustomView alloc]init];
self.view = view;
}
7.布局代码写在viewWillLayoutSubviews可以相对的安全点,viewDidDisappear在下个页面的viewWillAppear执行后才执行。view初始化执行了init还会执行initwithframe,一般业务放initwithframe里面就不会执行init
8.布局代码还是放layoutSubviews里面好
- (void)layoutSubviews{
[super layotSubviews];
//布局。。。
}
drawRect里 全局的堆栈stack,存放上下文CGContextRef 可以获取当前view的上下文。当前的绘制操作都会在最上层的上下文操作 所以不用add啥的。上下文pop出栈 push入栈。上下文和当前view相关联。可以手动push上下文 drawrect系统自动push了一个。view管理着CALayer,就是layer的delegate
setNeedsDisplay主动调用drawRect
setNeedsLayout主动调用layoutsubViews
上述两方法是异步调用 不是马上执行 在下一个runloop执行 所以做标记处利用 统一刷新
IB_DESIDNABLE 绘制可视化
tableview reloadData后调用了setNeedsLauout 不一定能马上拿到准确的contentSize
9.事件点击 后添加的先遍历。pointInside方法返回no就不会遍历他的subviews,触摸点在不在范围内。不再就遍历兄弟层级 在就遍历子view层级。hittest回来调用pointinside根据pointinside。
10.hitTest会调用pointInside 会根据pointInside的值做不同的处理 NO 返回nil YES 反序遍历subview
如果点击区域在自己这返回自己 如果在子view里返回子view
//先hitTest再pointInside
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return [super hitTest:point withEvent:event];
// 也可以自己做实现👇
//会调用pointinside 会根据pointinside的值做不同的处理。返回NO 返回nil 返回YES就会反序遍历子view
//如果点击区域在自己这里而不是在子view里面 返回自己;如果在子view里面 返回子view
//透明度<0.01 隐藏和enable都不接受点击事件
if ([self pointInside:point withEvent:event]) {//如果事件发生在范围内
//遍历子view
NSArray * subViews = [[self.subviews reverseObjectEnumerator] allObjects];//反序
for (UIView *subView in subViews) {
//转换坐标系,判断该点是否在bounds范围内
CGPoint convertPoint = [self convertPoint:point toView:subView];
if ([subView pointInside:convertPoint withEvent:event]) {
return subView;
}
}
return self;
}else{
return nil;
}
}
11.通过nextResponder来串联响应链 view->superview->viewController->view?->window->application
touchBegin就是这样一层层传递的(super touches begin...)
scrollview系统会重写他的super touchbegin。。导致传递不上去 self.nextResponder touchesbegin..
可以使得直接点击scrollview下面的按钮
12.tap pan switch等手势是根据touches begin touches moved touchescancel touches end 这四个方法判别<UIKit/Gesture....> button的不同点击状态也一样
默认情况下,手势识别出来了,会cancel掉view的touch事件
gesture.delayTouchesBegin = NO;默认 //yes 阻止touch方法的识别
gesture.cancelTouchesInViews = yes;默认
button的事件就是通过4种touch事件的组合来发送下面这个消息
[btn sendActionForControlEvents:UIControlEventTouchDown];//主动调用btn的点击事件
//当一个button超出俯视图范围时 让他可以点击
- (void)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
UIView *subView = self.subViews[0];
CGPoint convertPoint = [self convertPoint:point toView:subView];
if (CGRectContainsPoint(subview.bounds, convertPoint)) {
return YES;
}
return [super pointInside:point withEvent:event];
}
12.手势冲突经常会用到 [gestureTwo requireGestureRecognizerToFail:gestureOne]; //让后者优先级高
13.scrollview的clipstobounds默认yes 改成no可显示周边的.
scrollview通过nextResponder touchesbegin。。传递事件到下层
14.手势和touch冲突手势优先 用gesture.cancelsTouchesInView(这个属性的意思:/yes:手势识别了,会取消touch事件)处理可以都响应。手势识别出来会取消touch事件
gesture.view是手势绑定的view。手势代理方法可以在里面做处理获得touch.view
- tableview cell有重用池(没有显示在洁面上的cell)+现有池(显示在界面上的) 总数是不变的
table加载的时候cell的位置信息被保存起来了
16 自定义collectionViewLayout必须实现的三个方法prepareLayout(准备初始化每一个cell的布局). collectionViewContentSize. layoutAttributesForElementsInRect(返回一个数组获取每个cell的布局属性 就是frame).layoutAttributesForItemAtIndexPath(可以用自己的 用来设置每个cell的大小)
17 导航栏的透明度translucent设置为no就会使偏移量自动变成64 从导航栏下面开始布局。
18 .导航栏透明的方法 设置背景图片透明
- (void)transluentStyle{
[self.navigationController.navigationBar setBackgroundImage:self.image forBarMetrics:UIBarMetricsDefault];
}
- (UIImage*)image{
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
[[[UIColor whiteColor] colorWithAlphaComponent:0] setFill];
UIRectFill(CGRectMake(0, 0, 100, 100));
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
··
UIGraphicsEndImageContext();
return image;
}
去导航栏下面的黑线
// 方法一
self.navigationController.navigationBar.clipsToBounds = YES;
//方法二 遍历出来
- (UIImageView *)findBackLineImageV:(UIView*)view{
if([view isKindOfClass:[UIImageView class]] && view.frame.size.height <= 1){
return (UIImageView*)view;
}
NSArray *viewAry = view.subviews;
for (int i = 0; i < viewAry.count; i++) {
UIView *tmpV = [self findBackLineImageV:viewAry[I]];
if (tmpV) {
return (UIImageView*)tmpV;
}
}
return nil;
}
UIImageView *iamgeV = [self findBackLineImageV:self.navigationController.navigationBar];
iamgeV.hidden = YES;
barItems间距
NSMutableArray *barItems = [NSMutableArray array];
UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithTitle:@"Nav" style:UIBarButtonItemStylePlain target:self action:@selector(showNav)];
UIBarButtonItem *barItemSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
barItemSpace.width = 60;
UIBarButtonItem *barItemT = [[UIBarButtonItem alloc] initWithTitle:@"view" style:UIBarButtonItemStylePlain target:self action:@selector(showNavTwo)];
[barItems addObject:barItem];
[barItems addObject:barItemSpace];
[barItems addObject:barItemT];
self.navigationItem.rightBarButtonItems = barItems;
改变系统返回按钮
- (void)backArrowImage{
UIImage *image = [UIImage imageNamed:@"arrow.png"];
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.navigationController.navigationBar.backIndicatorImage = image;
self.navigationController.navigationBar.backIndicatorTransitionMaskImage = image;
}
self.navigationController.navigationBarHidden = YES;//这个方法把导航条remove了 并进行了位置操作
self.navigationController.navigationBar.hidden = YES;//把view属性进行操作
[self.navigationController.navigationBar setFrame:CGRectMake(0, 0, 414, 44)];
[navBarSuperView addSubview:self.navigationController.navigationBar];//下面的加着两句等效 会有bug 就这么一个原理
//用什么方式隐藏就要用什么方式打开
/*
A 转场到 B (A push B)
A的viewDidDisappear先执行 还是B的viewDidAppear先执行
push a的DidDisappear先执行(因为导航条有自己的背景 下面的window没有 反之会有黑条)
present b的viewDidAppear先执行
*/
18.

这种模式的hidesTabBar可以放在push方法里面.可以自己写个继承系统的UINavigationController。
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (self.viewControllers.count) {
viewController.hidesBottomBarWhenPushed = YES;
}
[super pushViewController:viewController animated:animated];
}
-(UIViewController *)popViewControllerAnimated:(BOOL)animated{
if (self.viewControllers.count == 2) {//pop前操作的所以是两个控制器
self.hidesBottomBarWhenPushed = NO;
}
return [super popViewControllerAnimated:animated];
}
// self.title = @"购物车";//切换时候会对tabbar和navibar的item进行刷新
self.navigationItem.title = @"购物车";
// 正确的设置 self.navigationItem.title来操作(因为self.title操作会影响tabbar的item)
tabbar(记得隐藏,因为爷控制器是tabbarViewCtr(儿的所有控制器默认都能看到tabbar))

- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.tabBarController.title = @"分类";//一个根navi套tab时候标题这么写
//正确的设置 self.tabBarController.title来操作(如果self.title操作会影响tabbar的item,但对navbar没有效果,因为导航条属于爷控制器)
}
19.启动页延时操作
/*
登录界面,如果没有保存登录账号和密码,那么第一个界面登录界面,
如果保存了登录账号和密码,直接显示主页
第一个界面不确定的话,不要修改window的根控制器(不要修改框架结构);
判断是否 push/present 登录界面(如果展示就利用启动页延时)
如果跟控制不确定,后面很多逻辑都要加if或者判断 (打个比方,回到mainMenuTabBarVCtr的主页)
这样不用修改根控制器 保持结构不变
*/
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self styleTwo];
[self delayLaunchImage];
return YES;
}
// 耦合度
- (void)delayLaunchImage{
eocWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
eocWindow.rootViewController = [UIViewController new];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
[imageView setImage:[UIImage imageNamed:@"11.PNG"]];
[eocWindow addSubview:imageView];
[eocWindow makeKeyAndVisible];
[self performSelector:@selector(cancelLaunImage) withObject:nil afterDelay:5];
//任务做好了做移除操作
}
- (void)cancelLaunImage{
[eocWindow resignKeyWindow];
eocWindow = nil;
}
20.mvvm 采用双向绑定:view变动的时候自动反应到viewModel上,viewModel的变动,自动反应到view上。model和view不直接打交道

mvp 各部分的通信都是双向的互相调用方法,view和model不发生联系,都通过presenter来处理,view不处理任何业务逻辑,就是单单的view称之为被动试图,没有任何主动性,导致p比较重量级,所有的逻辑都在这里

/*
V - VM
MVCC (多控制器)addChildViewController
对业务的拆分(细分c)
*/
详见代码
21.路由层

不是很必要 了解就行 规则的制定 用url
22.图片压缩
- (void)imageDataLoad{
// png 文件属性格式并不会压缩,压缩的是图片内容(像素)
NSData* pngImageData = UIImagePNGRepresentation(_albumImage);
NSData *jpgImageData = UIImageJPEGRepresentation(_albumImage, 0.1);//0.1是jpg提供的压缩的比例
_pngImageV.image = [UIImage imageWithData:pngImageData];
_jpgImageV.image = [UIImage imageWithData:jpgImageData];
// jpg
NSLog(@"png::%@", [self length:pngImageData.length]);
NSLog(@"jpg::%@", [self length:jpgImageData.length]);
}
// bitmap
- (UIImage*)scaleImage:(UIImage*)image size:(CGSize)imageSize{
UIGraphicsBeginImageContext(imageSize);
[image drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
图片渲染
/*
从图片文件把 图片数据的像素拿出来(RGBA), 对像素进行操作, 进行一个转换(Bitmap (GPU))
修改完之后,还原(图片的属性 RGBA,RGBA (宽度,高度,色值空间,拿到宽度和高度,每一个画多少个像素,画多少行))
//256(11111111)
/
图片过滤
/
从图片文件把 图片数据的像素拿出来(RGBA), 对像素进行操作, 进行一个转换(Bitmap (GPU))
修改完之后,还原(图片的属性 RGBA,RGBA (宽度,高度,色值空间,拿到宽度和高度,每一个画多少个像素,画多少行))
//256(11111111)
*/
渲染到屏幕会转化成bitmap 获取文件的宽度 高度等 算出相应的内存大小 渲染到屏幕里来
渲染时候需要按照bitmap规格来屏幕像素乘4 /1024/1024 所以内存会很大
优化 尽量用上下文生成一个新的文件
23.block
//打印出tmpBlock的类的层级:__NSMallocBlock__ 、__NSMallocBlock 、NSBlock、NSObject
- (void)logBlockIsObject {
int i = 10;
void (^tmpBlock)(void) = ^{
NSLog(@"i = %d",i);
};
Class blockClass = object_getClass(tmpBlock);
while (blockClass) {
NSLog(@"class = %@",blockClass);
blockClass = class_getSuperclass(blockClass);
}
}
//block的内存分布:block有三种种类:globalBlock、mallocBlock堆、stackBlock
//1.没有外部变量的block:统统属于globalBlock
//2、有外部变量的情况:
//2.1全局变量、全局静态变量、局部静态变量(统统都是globalBlock);
//2.2普通的外部变量: strong和copy修饰的是一样的,为mallocBlock;weak的,为stackBlock,在栈区
//3、如果block作为自定义函数参数,block在栈区;系统的函数,会把block转移到mallocblock,也就是堆区
- (void)testBlock:(testBlock)block { //你自定义的函数,block作为参数,block会是在栈区;但是如果你调用了系统的方法,block它会自动copy,变成mallocblock
NSLog(@"testBlock %@", object_getClass(block));
}
//
- (testBlock)getBlock {
int i = 10;
return ^{
NSLog(@"i = %d", i);
};
}
//block结构:
//转换成C++代码命令:xcrun -sdk iphonesimulator11 clang -rewrite-objc ViewController.m
//xcodebuild -showsdks 展示sdks
//包含weak类型数据转换成C++:xcrun -sdk iphonesimulator11.0 clang -S -rewrite-objc -fobjc-arc -fobjc-runtime=ios-11.0 ViewController.m
可以看到cpp文件
struct block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct block_desc_0 {
size_t reserved;
size_t Block_size;
} __block_desc_0 = { 0, sizeof(struct block_desc_0)};
struct block_impl_0 {
struct block_impl impl;
struct block_desc_0 *Desc;
block_impl_0 (void *fp,struct block_desc_0 *desc, int flags=0) {
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void block_func_0 (struct block_impl_0 *__cself) {
printf("八点钟学院");
}
void test () {
// int i = 10;
// static int tmpStaticValue = 10000000;
struct block_impl_0 implTest = block_impl_0((void *)block_func_0, &__block_desc_0); //创建一个结构体
void (*impTestPointer)() = (void (*)())&implTest; //强制转换 (void (*)())类型
block_impl *tmpPointer = (block_impl *)impTestPointer; //强制转换成(block_impl *)类型
void (*Func)(block_impl *) = (void (*)(block_impl *))tmpPointer->FuncPtr; //获取函数指针
Func(tmpPointer); //调用函数
// printf("i = %d\n", i);
}
两个struct类型强转化能运行成功是因为结构体地址的头部地址块都是同一个结构体block_impl(地址偏移一致) 所以可以强转后调用方法访问地址
block引入外部变量一般不能在里面修改 个人理解是系统认为结构体是个构造函数初始化的 变量作为参数传进来本身是不让你改变的,会引起歧义
block -> 结构体 -> 构造函数创建 ->外部变量不想给你修改吧?
对象不能改变 对象的属性可以改变
成员变量传进去的是self 类本身在c++里头就是结构体
传进去的东西本身是不能修改的,但它的属性是可以修改的
主要还是下面这块
struct block_impl_0 {
struct block_impl impl;
struct block_desc_0 *Desc;
int i;
block_impl_0 (void *fp,struct block_desc_0 *desc, int _i,int flags=0):i(_i) {
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__block修饰之所以可以修改是因为把变量转化成了结构体 之后把结构体的地址传了进去。原来不能修改是因为传的是值进去 现在传地址就可以了。因为block是堆里的 变量在栈里 栈里的a其实修改是在修改堆里面的值 结构体中的forwarding指针是关键
保证堆栈的统一
全局变量的话构造函数里传都不用传
局部静态变量传的是地址
成员变量抓去的是self 废成员变量抓去的就是本身 方法就是执行函数
//会查找block 系统初始化啥的会有一些系统的block
// Copy, or bump refcount, of a block. If really copying, call the copy helper if present.
void *_Block_copy(const void *arg) { //栈区的block
struct Block_layout *aBlock;
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) { //block在堆区,只要把block引用计数+1就可以了
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) { //block是全局的,返回本来的值就行了
return aBlock;
}
else {
// Its a stack block. Make a copy.
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return NULL;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
///定位自己写的block,下面这句话如果打印出来有main_block_invoke就是我们的block
backtrace_symbols_fd(&result->invoke, 1, 1); //自己添加的
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed390271725
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
result->isa = _NSConcreteMallocBlock;
printf("result address: %p", result);
return result;
}
}
创建堆区的block就会用到上一节结构体里面那个desc的block大小
-fno-objc-arc 不启用arc
block_copy把栈区的block放到堆区
_Block_object_assign mrc才调用 arc不会调用
runtime _block_copy里面的实现了具体从栈到堆的拷贝
通过malloc创建一个block的堆 赋值 引用计数变1 然后mrc的话对象引用计数+1
block有普通变量:block_copy:把block从栈放到堆
block里面有__block变量:block_copy把block从栈放到堆,并把__block的结构体从栈放到堆,同时改变forwarding的指向
//__block的作用是把变量生成的结构体从栈复制到堆里 利用forwarding指针 偏移量这些对变量进行修改
block分析
- (void)weakCycleBlockFunction {
NSObject *otherObject = [[NSObject alloc] init];
NSLog(@"object %p &object %p", otherObject, &otherObject);
/* 脑海里猜想block的结构
struct blk {
__strong otherObject;
func()
}
func() {
NSLog(@"object %p ", blk->otherObject);
}
*/
blk = ^{
NSLog(@"object %p &object %p", otherObject, &otherObject);
};
// blk();
otherObject = nil;
blk();//不为nil 因为blk还堆obj强引用的
}
- (void)weakCycleBlockFunctionTwo {
NSObject *otherObject = [[NSObject alloc] init];
NSLog(@"object %p &object %p", otherObject, &otherObject);
__weak typeof(otherObject)weakObject = otherObject;
blk = ^{
__strong typeof(weakObject)strongObject = weakObject;
NSLog(@"object %p &object %p", strongObject, &strongObject);
};
/* 脑海里猜想block的结构
struct blk {
__weak otherObject;
func()
}
func() {
__strong tmpObject = blk->otherObject
NSLog(@"object %p ", blk->otherObject);
}
*/
blk();
otherObject = nil;
blk();
}
- (void)weakCycleBlockFunctionThree {
NSObject *otherObject = [[NSObject alloc] init];
// NSLog(@"object %p &object %p", otherObject, &otherObject);
__weak typeof(otherObject)weakObject = otherObject;
blk = ^{
__strong typeof(weakObject)strongObject = weakObject;
if (strongObject) {
NSLog(@"object %p &object %p 111", strongObject, &strongObject);
sleep(3);
NSLog(@"object %p &object %p 222", strongObject, &strongObject);
}
};
/* 脑海里猜想block的结构
struct blk {
__weak otherObject;
func()
}
func() {
__strong tmpObject = blk->otherObject
NSLog(@"object %p ", blk->otherObject);
}
*/
blk();
otherObject = nil;
sleep(5);
blk();
}
- (void)strongCycleBlockFunction {
object = [[NSObject alloc] init];
// NSLog(@"object %p &object %p", object, &object);
//对于成员变量,block是把self引用计数+1,不是对成员变量object本身来增加引用计数的
blk = ^{
NSLog(@"object %p &object %p", object, &object);
};
/*
struct blk {
__strong self;
func();
};
func(){
NSlog(@"%@",self->object);
}
*/
blk();
object = nil;
blk();
}
27.线程
NSThread:
一线程对象 对应一个线程,当执行完之后,不能重新开启nsoperation也是这样
// 主动开线程
[NSThread detachNewThreadSelector:@selector(threadOne) toTarget:self withObject:nil];
//手动开启
_thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTwo) object:nil];
[_thread start];
NSOperation:
NSBlockOperation
operation 也可以手动开启
block并不是先添加就先执行,当operation执行,就不能在添加block
NSBlockOperation里面block块全部结束,那么这个NSBlockOperation才算结束(即finished = YES)
//运用 :多个任务处理(任务A,任务B,任务C)三个任务完成了,通过KVO监听得到(isFinished) (任务是block块)
手动开启的时候,会分配一个到主线程上执行
*/
/*
start 没有重复开启: 因为在start方法里面对blockOperation的状态finish状态进行判断,
如果finish=YES,就不会执行了
main 直接是掉用block
需要注意的点
*/
[blockOperation start];
// [blockOperation main];
NSInvocationOperation:
NSInvocation可以吧方法对象化
// 3 selector 方法签名(方法的对象结构,相关的结构信息:返回值,调用者,方法名,参数)
NSMethodSignature *signture = [self methodSignatureForSelector:@selector(name:age:sex:)];
// 2 signture
invation = [NSInvocation invocationWithMethodSignature:signture] ;
invation.target = self;
invation.selector = @selector(name:age:sex:); //和签名的seletor要对应起来
// 配置参数
NSString *name = @"eoc";
NSString *age = @"2";
NSString *sex = @"男";
[invation setArgument:&name atIndex:2];
[invation setArgument:&age atIndex:3];
[invation setArgument:&sex atIndex:4];
//[invation getReturnValue:];
// 1 需要 invation
_invocationOperation = [[NSInvocationOperation alloc] initWithInvocation:invation];
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
__unsafe_unretained NSString *returnValue;
[invation getReturnValue:&returnValue];
NSLog(@"returnValue:%@", returnValue);
}尽量不要用有返回值的。因为不强引用 可能被释放
要自定义NSOperation需要重写start和main方法
start方法重写需要对finished赋值yes才会走dealloc.
start方法里面手动调用main方法
(start一般用来根据finished处理异常 主要任务放到main里面操作)
/*
dealloc 没被执行,是因为CustomOperation的状态为未完成状态 _finished = NO;
self.finished属性是仅读的,
*/
因为是只读的 所以需要重写:
@synthesize finished = _finished;
- (void)main{
// 主要的业务逻辑放到这里处理
NSLog(@"main2");
_finished = YES;
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeCount) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];//这样定时器才能跑起来
}
- (void)start{
//异常处理
NSLog(@"%@", [NSThread currentThread]);
//[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
// self.finished = YES
//防止手动多次调用出现的问题
if (_finished) {
return;
}
if (self.isExecuting) {
return;
}
NSLog(@"start");
[self main];
}
//NSOperation就可以吧整个线程放进一个文件 NSThread基本上就是一个方法。NSOperation可以 addDependency设置依赖 先后关系也可以自己监听finished完成
线程一旦开启 不能真正的取消 只是一个节点 状态 我们需要根据这个节点状态自己设置 return
- (void)threadTwo{
NSLog(@"threadTwo::%@", [NSThread currentThread]);
for (int i = 0; i < 5; i++) {
if([NSThread currentThread].isCancelled){
return;
}
sleep(2);
NSLog(@"%d", i);
// 取消,正在要取消线程要设置取消节点
if([NSThread currentThread].isCancelled){
return;
}
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[_thread cancel];
}
28.串行 并行是修饰队列的
并发是针对任务async,可以同时开启多个任务的 同不同时需要看队列
// 组 多个任务执行
- (void)group_gcd{
// 1 创建组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_CONCURRENT);
// 2向组添加任务 任务数count=3
dispatch_group_async(group, queue, ^{
NSLog(@"task one::%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"task two::%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"task three::%@", [NSThread currentThread]);
});
// 3组任务全部完成了,就通知 任务数count为0
dispatch_group_notify(group, queue, ^{
NSLog(@"finish all task");
});
}
//一个界面加载多个网络请求 可以用这个 👆
- (void)groupStyleTwo{
// 1 创建组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_CONCURRENT);
// 填加一个空任务
for (int i = 0; i < 3; i++) {
dispatch_group_enter(group); // 任务数+1
dispatch_async(queue, ^{
[self netLoadSync:i]; // 如果在这个任务再开一个线程,那么不能保证你的需求
dispatch_group_leave(group);// 任务数-1
});
}
// 3组任务全部完成了 为0的时候,就通知
dispatch_group_notify(group, queue, ^{
NSLog(@"finish all task");
});
}
// task 同步
- (void)netLoadSync:(int)taskCount
{
NSString *urlstr = [NSString stringWithFormat:@"%@?versions_id=1&system_type=1", URLPath];
NSURL *url = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
//信号量做一个同步的效果
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error){
// NSDictionary *infoDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"完成了,taskcount:%d", taskCount);
dispatch_semaphore_signal(sema);
}];
[task resume];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
NSLog(@"finish 代码跑完了:%d",taskCount);
}
barrier
//栅栏
- (void)barrier_fct{
dispatch_queue_t queue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"分界线前:taskOne");
});
dispatch_async(queue, ^{
NSLog(@"分界线前:taskTwo");
});
dispatch_async(queue, ^{
NSLog(@"分界线前:taskThree");
});
dispatch_barrier_async(queue, ^{ // 分界线里面,queue可以看作是串行的,当前只能执行barrier里面的task
NSLog(@"分界线里面的任务");
});
dispatch_async(queue, ^{
NSLog(@"分界线后:taskFour");
});
dispatch_async(queue, ^{
NSLog(@"分界线后:taskFive");
});
}
//👆 用途
/* 读写数组 mutableAry,如果开几个线程来操作数组
1 写数组(移除index=0对象), 2 写数组(移除index=0的对象) // 有问题
1 读数组 , 2 读数组 // 没问题
1 读数组 2写数组
只要涉及到写操作(要做保护) 读的话不大需要
*/
_safeAry = [NSMutableArray array];
[_safeAry addObject:@"0"];
[_safeAry addObject:@"1"];
[_safeAry addObject:@"2"];
[_safeAry addObject:@"3"];
rwQueue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_CONCURRENT);
- (void)testRWAry{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 20; i++) {
// 读
dispatch_async(queue, ^{
NSLog(@"%d::%@", i, [self indexTo:i]);
});
// 写
dispatch_async(queue, ^{
[self addObject:[NSString stringWithFormat:@"%d", i+4]];
});
}
}
// 写 保证只有一个在操 作(避免了同时多个写操作导致的问题)
- (void)addObject:(NSString*)object{
dispatch_barrier_async(rwQueue, ^{
if (object != nil) {
[_safeAry addObject:object];
}
});
}
// 主队列 mainqueue--》 主线程 mainThread
// 注意同步,因为业务关系,必须马上数据
- (NSString*)indexTo:(NSInteger)index{
__block NSString *result = nil;
dispatch_sync(rwQueue, ^{ //如果是异步那就会返回nil了 不行
if (index < _safeAry.count) {
result = _safeAry[index];
}
});
return result;
}
重复
// 重复 执行任务
- (void)apply_gcd{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(5, queue , ^(size_t count) {//类似for循环
NSLog(@"%d", count);
});
}
延后
// 延后
- (void)after_GCD{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"start:%@", [NSThread currentThread]);
dispatch_after(1, queue, ^{
NSLog(@"dispatch_after:%@", [NSThread currentThread]); //其实又开启了一个线程
});
NSLog(@"end");
});
}
激活
// 激活
- (void)queueInactive{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT_INACTIVE);//往这个队列里添加任务不会呗执行
dispatch_async(queue, ^{
NSLog(@"start:%@", [NSThread currentThread]);
});
dispatch_activate(queue);//需要这个来手动激活
}
f_IMP
void testFunction(){
NSLog(@"testFunction::-->%@", [NSThread currentThread]);
}
- (void)function_f{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT);
dispatch_async_f(queue, nil, testFunction);//传的不是block 而是方法.中间那个放参数
}
定时器
- (void)timeSource{
// dispatch_object_t;
// 1 创建一个队列
dispatch_queue_t eoc_queueOneTT = dispatch_queue_create("eoc_queueOneTT", DISPATCH_QUEUE_SERIAL);
// io source关联到队列
soure = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 配置soure的时间
dispatch_source_set_timer(soure, DISPATCH_TIME_NOW, 1, 1);
// 配置source的处理事件
dispatch_source_set_event_handler(soure, ^{
NSLog(@"soure_event:==%@", [NSThread currentThread]);
});
// 开启定时器
dispatch_resume(soure);
}
网友评论