- LuckySXyd
-
不一定
比如监听器(Observer)就没有被释放, 需要你在dealloc中手动释放它们
一般的属性都能够自动释放
ios dealloc什么时候调用
Objective-c 语言中最头疼的事就是内存释放,申明一个变量后记得一定要释放这个变量,在我的博客中已经有一些这方面的文章,我们定义的全局变量都是在 - (void)dealloc 函数中释放的; 里面继承了一个[super dealloc]方法, 有些同学平时自己释放内存都是写在 [super dealloc]的后面,但是在Objective-c 中不能这样写,所有的释放都必须写在 [super dealloc]的前面。 -------错误的写法-------- - (void)dealloc { [super dealloc]; [XXX release]; ...... } -------正确的写法-------- - (void)dealloc { [XXX release]; [super dealloc]; ...... } 原因是:“你所创建的每个类都是从父类,根类继承来的,有很多实例变量也会继承过来,这部分变量有时候会在你的程序内使用,它们不会自动释放内存,你需要调用父类的 dealloc方法来释放,然而在此之前你需要先把自己所写类中的变量内存先释放掉,否则就会造成你本类中的内存积压,造成泄漏”.不过在IOS6有了ARC后就不用手动去释放了,也没有此函数了!2023-06-16 04:45:371
iOS开发-对象什么时候dealloc?dealloc发生在哪个线程?
当对象的引用计数减为0时候。 执行结果: dealloc并不总是在主线程中被调用,,其调用线程为最后一个调用release方法的线程。 也就是说,dealloc方法有可能在任何线程被调用。2023-06-16 04:45:441
iOS dealloc底层深入(dealloc做了什么,为什么弱引用不会导致循环引用)
源码:objc4-723,地址: https://opensource.apple.com/tarballs/objc4/ 调用流程:首先调用 _objc_rootDealloc() -> 接下来调用 rootDealloc() -> object_dispose()-> clearDeallocating() 一、调用 _objc_rootDealloc() 二、调用 rootDealloc() 2.1、首先判断 isTaggedPointer 是否是标记指针 是直接 return ; 注:Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储( https://www.jianshu.com/p/70551a5e77c0 ) 2.2、其次判断该对象是否可以被快速释放。一共有5个判断依据: nonpointer 是否优化过isa指针(类似Tagger Pointer) weakly_reference 是否存在弱引用指向 has_assoc 是否设置过关联对象 has_cxx_dtor 是否有c++的析构函数(.cxx_destruct) has_sidetable_rc 引用计数器是否过大无法存储在isa中(使用 sidetable 来存储引用计数) 三、 object_dispose() 如果不能快速释放,则调用 object_dispose()方法,做下一步的处理(调用objc_destructInstance) 3.1、判断是否存在c++的析构函数,有则调用object_cxxDestruct() 3.1.1、object_cxxDestruct() object_cxxDestruct这个方法最终会调用objc_storeStrong来释放成员变量(实例变量) 3.2、移除关联对象_object_remove_assocations(常用于category中添加带变量的属性),关联对象定义: https://www.cnblogs.com/limicheng/p/4186214.html 3.3、调用clearDeallocating()方法 sidetable_clearDeallocating和clearDeallocating_slow,并最终都调用:weak_clear_no_lock,该方法将所有指向所提供对象的所有弱指针置清空。 总结:clearDeallocating一共做了两件事 将对象弱引用表清空,即将弱引用该对象的指针置为nil 清空引用计数表(当一个对象的引用计数值过大(超过255)时,引用计数会存储在一个叫 SideTable 的属性中,此时isa的 has_sidetable_rc 值为1),这就是为什么弱引用不会导致循环引用的原因2023-06-16 04:45:511
为什么ViewController释放后不走dealloc
在一个项目中,如果ViewController使用完成之后,发现这个东东并没有释放掉,dealloc方法不走,看着那个内存蹭蹭的网上增,就跟自己的火气一样。相信大家都知道如何去释放一个不用的ViewController,但是还是有些其他因素限制了内存释放。首先,如果你创建了一个VController,使用它却没释放它,那这个VC肯定存在,那dealloc指定不调用,那么你创建的类就会内存泄露textViewController * text = [[textViewController alloc] init];[self.navigationController pushViewController:text animated:YES];//[text release];然后,如果你的VC中有NSTimer,那么就要注意了,因为当你[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];时,这个 target:self 就增加了VC的RetarnCountr如果你不将这个timer invalidate,就别想调用dealloc。再然后,一个比较隐蔽的因素,你反过头去找找看,跟这个类有关的代理,嗯,对是代理,有没有强引用的属性啊?对,比如一个代理的delegate应该是 assign 的现在是retain,就是这个,它会影响你不让你调用dealloc,不信,就试试吧。最后,如果以上都没有问题的话,那么,真问题就来了。我就遇到了这种情况,在使用ASI进行网络请求的时候,因为需求原因,我使用属性将名为 ASIFormDataRequest 的NSOperation 标记住了,就将上面的问题找了又找,就是不行,最后是将那个标记的属性置为 nil 才解决了这个不调用 dealloc 的这个蛋疼问题。所以,如果你遇到了比较隐蔽的原因,那就去找找你自己控制不了的因素,就像这个第三方。如果你不了解它的运行机制,那就一定要注意这个库对你程序的影响。嗯,善用XXX.delegate = self;PS:dealloc中的释放也是有顺序的,就好比创建时,先父类,再子类,释放的时候反过来,不然有几率会crash,至于原因。 子类是父类的继承,比较NB,以至于要杀死他们的时候应该先干掉比较牛B的子类。2023-06-16 04:45:581
引用计数器
对象一出生引用计数器就为1 一个对象引用计数器为0时才会被释放 给一个对象发送一条retain 计数器+1 发送一个release给对象,计算器-1 dealloc方法 一般会重写dealloc方法,在这列是方法相关资源,dealloc就 //问题1:什么是引用计数器? 答: 对象被指针指向或被使用的次数 每个OC对象都有自己的引用计数器,它是一个整数,表示有多少人正在用这个对象 //问题2:引用计数器的作用? 答: 计算对象被使用次数 当使用alloc、new或者copy创建一个对象时,对象的引用计数器默认就是1 当对象的引用计数器为0时,对象占用的内存就会被系统回收 如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收(除非整个程序已经退出 ) //问题3:怎么操作引用计数器? 答: 给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身 给对象发送一条release消息, 可以使引用计数器值-1 给对象发送retainCount消息, 可以获得当前的引用计数器值 需要注意的是: release并不代表销毁回收对象, 仅仅是计数器-1 alloc new copy retain 会增加对象的引用计数器一次 release能减少对象的引用计数器一次 //问题4:dealloc 方法的作用? 当对象的引用计数器为0时,对象就会被释放,在对象被释放前就会调用这这个方法 对象即将被销毁时系统会自动给对象发送一条dealloc消息 (因此, 从dealloc方法有没有被调用,就可以判断出对象是否被销毁)2023-06-16 04:46:061
ios dealloc什么时候调用
当对象被真正销毁的时候,会调用这个方法。有些时候,感觉上是销毁了某个对象,比如pop返回操作,但是发现被pop的控制器没有调用dealloc方法,说明了这个控制器没有被销毁,要么被强引用了,要么被循环引用了,依旧在内存中。2023-06-16 04:46:131
为什么控制器dealloc后内存并没有释放
16年外交部发2023-06-16 04:46:202
iOS pop 了为什么不走dealloc方法
那就说明你的控制器没有释放没有释放就说明有强指针引用着它。检查:1、你的类中有没有用strong引用着控制器;2、代理要用 weak修饰, 不可用srong;3 、block是否用__block或 __weak修饰了, 没有的话请加上2023-06-16 04:46:272
ios cell会dealloc吗
任何一个对象被销毁时系统都会调用dealloc方法cell也会2023-06-16 04:46:351
uiview的dealloc 什么时候调用
最近的一个项目, 基于UINavigationController, 发现个奇怪的问题, 从ViewController (A) push 到ViewController (B) 之后, 发现内存有所增加, 再点左上角的返回按钮回到(A)后, 内存一直在增加, nslog之后发现(B)的viewDidUnload方法根本没有调用, 更别说dealloc, 但viewDidDisappear还是调用了的. google了一番, 终于找到答案: ht tp:/ /stackoverfl ow.c om/questions/55944 10 /viewdidunload-is-not-getting-called-at-allAs others have mentioned, viewDidUnload is a method of UIViewController. It isn"t guaranteed to get called. It only gets called in low memory situations when the app releases the controller"s view to free up memory. This gives you an opportunity to release any of the view"s subviews that you may have retained as properties of your controller.If you"re calling removeFromSuperview on your view controller"s view, then you"re probably using UIViewController in a context it wasn"t designed to handle. View controllers are designed to manage full-screen views. These views are expected to be presented in just a handful of contexts on the iPhone:As the full-screen view of the window"s root view controllerAs a full screen view in a hierarchy of screens managed by UINavigationControllerAs a full screen view presented within a tabAs a full screen view presented using presentModalViewController:animated:As long as you"re using the view controller in these contexts (plus a couple other contexts on iPad), then you can reliably manage your view"s life-cycle via the loadView, viewDidLoad, viewWillAppear:animated:, viewDidAppear:animated:, viewWillDisappear:animated:, and viewDidDisappear:animated:, and viewDidUnload methods (again, bearing in mind that viewDidUnload won"t always get called).The only time you should typically pass a view controller"s view to addSubview: is when you add your root view controller"s view to the window. If you want to try to use a nested view controller to manage a non-fullscreen subview, you"ll have to call its viewWill/DidAppear/Disappear:animated: and viewDidUnload methods manually at appropriate times from your full-screen view"s controller.htt p:/ /stackoverflo w.c om/questions/5124305/iphone-uiviewcontroller-class-dealloc-function-never-calledInterface Builder caches nibs until there"s a memory warning, or it exhausts its cache. At such time, it will reclaim any memory that it was using.于是模拟了一下内存警告, 发现(B)的viewDidUnload方法确实立即被调用了, 如何我push, pop这样来回多次, 当模拟内存警告后, (B)的viewDidUnload方法也会被调用多次, 然而dealloc却还是没有调用.2023-06-16 04:46:431
iOS开发中,如果使用ARC,还用在dealloc里面把声明的一些属性和成员变量置成nil吗?
使用ARC后,系统会自动进行垃圾回收,所以dealloc就不用释放内存了,但是系统回首机制有一定的延迟性,如果项目较大或者对内存管理要求比较高,建议使用手动管理内存。使用ARC时通常使用alloc/init方法创建对象2023-06-16 04:47:022
ios开发中如果没有在dealloc中移除通知会造成什么后果
通过是先有系统接收后,在由用户操作后,由系统发送到你的app中,这个事当你app离线的时候。 如果你的app处于活动状态,那么及时接收就会及时处理掉了该消息,不会再通知里面。 不知道你是做为开发者角度提这个问题,还是用户?2023-06-16 04:47:091
dtrace 怎么检查ios 的内存泄露
ios怎么查看内存泄露,有以下几种方法供大家参考:1.静态分析通过静态分析我们可以最初步的了解到代码的一些不规范的地方或者是存在的内存泄漏,这是我们第一步对内存泄漏的检测。当然有一些警告并不是我们关心的可以略过。2.通过instruments来检查内存泄漏这个方法能粗略的定位我们在哪里发生了内存泄漏。方法是完成一个循环操作,如果内存增长为0就证明我们程序在该次循环操作中不存在内存泄漏,如果内存增长不为0那证明有可能存在内存泄漏,当然具体问题需要具体分析。3.代码测试内存泄漏在做这项工作之前我们要注意一下,在dealloc的方法中我们是否已经释放了该对象所拥有的所有对象。观察对象的生成和销毁是否配对。准确的说就是init(创建对象的方法)和dealloc是否会被成对触发(简单说来就是走一次创建对象就有走一次dealloc该对象)。下面是自己遇到的一些比较隐秘的造成内存泄漏的情况:1.两个对象互相拥有:也就是说对象a里面retain/addSubview了b对象,b对象同时也retain/addSubView了a对象。注意:delegate不要用retain属性,要用assign属性也会导致互相拥有。2.有时候需要用removeFromSuperView来释放:具体说明,也许我的a对象拥有一个b对象,b对象add到了c对象上,而在我们的设计中b对象的生命周期应该和a对象相同;这时候只一句[brelease]/self.b=nil是不能把b对象释放掉的(一般情况下release会使其retainCount-1,[superdealloc]会再次将所有subView的retainCount-1,而b并不是a的subView,所有最后的一次-1没有了);所以我们需要在之前加上[bremoveFromSuperView]。2023-06-16 04:47:161
ios dealloc 调用self.view 会移除掉么
iOS程序退出的时候dealloc函数不会被调用,ios关闭一个应用已经自动把内存释放了 的。2023-06-16 04:47:231
ios应用开发Objective-C内存管理基础
对于我们.net开发人员来说,.net为我们提供了自动内存管理的机制,我们不需去关心内存的管理。但是iPhone开发中却是不能的。这篇文章将简述一下Objective-C的内存管理机制和方法和一些特性。手动的进行内存管理Cocoa和Objective-C的类都是NSObject的子类。NSObject中有几个方法进行内存管理。alloc方法为对象分配一片内存空间。dealloc方法用于释放对象的空间。但是在我们的代码中将永远都不会使用dealloc方法,因为运行时会为你调用此方法释放内存空间。而你需要做的只是引用计数,稍后介绍什么是引用计数。除了alloc和dealloc,NSObject的还有retain和release方法两个方法用于引用计数。retain方法给retainCount变量加1,release方法给retainCount变量减1。当使用alloc为对象分配一片内存空间的时候,retainCount会为1。在这个对象的生命周期内,这个对象可能继续被其它变量引用。但有新的变量指向这个对象的时候,你应该调用retain方法,这样运行时才会知道有新的引用指向了这个变量,在这个对象生存期中拥有它的使用权。这个被Objective-C开发人员称之为“拥有”。例如:Foo*myFooOne=[[Fooalloc]init];//retaincount为1Foo*myFooTwo=myFooOne;//myFooTwo指向了这个对象//retaincount仍然为1[myFooTworetain];//调用retain方法,运行时才知道myFooTwo指向了该对象,retaincount为2上面的代码中,myFooTwo通过调用retain方法,取得了Foo对象的拥有权。在这个对象的生命周期中,会有很多变量来指向和引用它。指向这个对象的变量也可以通过release方法来解除这种拥有权。release方法将会告诉运行时,我已经使用完这个变量了,已经不需要它了,retainCount计数减1。当对象的retainCount的计数大于或者等于1的时候,运行时会继续维持这个对象。当对象的retainCount为0的时候,运行时会释放这个对象,并回收它占得内存空间。下图展示了一个Foo对象的生命周期。Foo对象首先在内存中分配一个内存空间,并且被myFooOne引用。在这个时候Foo对象的retaincount为1。Foo * myFooOne = [[Foo alloc] init];第二个引用变量指向Foo对象,这个引用变量接着调用retain方法,其实也是调用Foo对象的retain方法。Foo对象的retaincount变成2。Foo*myFooTwo=myFooOne;[myFooTworetain];接着当myFooOne引用不需要的时候,通过调用release方法,解除与Foo对象的拥有权,Foo对象的retaincount变成1。[myFooOnerelease];但myFooTwo不在需要的时候,同样通过调用release方法,解除与Foo对象的拥有权,Foo对象的retaincount变成0。内存泄露我们经常会在一个方法中声明对象,看下面这个例子:-(void)myMethod{//incorrectmethodNSString*myString=[[NSStringalloc]init];//retainCount=1Foo*myFoo=[[Fooalloc]initWithName:myString];//retainCount=1NSLog(@"Foo"sName:%@",[myFoogetName]);}这上面这个方法中,我们为myString 和myFoo分配了内存空间。方法执行结束之后,两个变量超出了作用域的范围,所以不再有效。但是这个方法并没有releases这两个对象。所以运行时没有释放这两个变量占据的内存空间。除非你的应用程序结束,否则这两个变量占据的内存空间一直都是不可用的。我们把它称之为内存泄露。为了防止内存泄露。无论什么时候,我们创建一个对象,或者创建一个对象的拷贝,我们都必须通过release方法释放。-(void)myMethod{NSString*myString=[[NSStringalloc]init];//retainCount=1Foo*myFoo=[[Fooalloc]initWithName:myString];//retainCount=1NSLog("Foo"sName:%@",[myFoogetName]);[myFoorelease];//retainCount=0sodeallocate[myStringrelease];//retainCount=0sodeallocate}弱引用看下面的例子:-(void)myMethod{//anincorrectmethodFoo*myFooOne=[[Fooalloc]initWithName:@"James"];//retainCount=1Foo*myFooTwo=myFooOne;//retainCountstill1[myFooOnerelease];//retaincount=0sodeallocatedNSLog("Name:%@",[myFooTwoprintOutName]);//runtimeerror}nyFooTwo指向了Foo对象,但是没有调用retain方法,就是一种弱引用,上面的代码会在运行时报错。因为myFooOne调用release方法。retaincount变成0,运行时,回收了对象的内存空间。然后myFooTwo调用printPutName自然就报错了,见下图说明。总结:本文简单的介绍了一下手动的进行内存管理、内存泄露、弱引用等Objective-C的知识。2023-06-16 04:47:311
通知不在dealloc中removeobserver为什么会崩溃
原因分析:ios的webview内核设定了其在进行momentumscrolling(弹性滚动)时,会停止所有的事件响应及DOM操作引起的页面渲染(亲测),故onscroll不能实时响应曾做兼容方案:使用ontouchmove去替代nscroll,虽然能更频繁的触发事件,但是这边的项目2023-06-16 04:47:391
iOS创建添加通知、发送通知、移除通知
1、创建通知,最好在viewDidLoad的方法中创建 2、发送通知 3、移除通知,由那个控制器创建由那个控制器移除,谁创建谁移除,最好在dealloc方法中移除,如果通知不能及时的移除掉,当下次进入该控制器时会重复创建NSNotificationCenter,在对应方法中发送通知给上一次创建的通知,但是上一个通知所在的控制器已被干掉,所以这时候就会报错 这里注意:如果dealloc方法不调用,说明当前有变量没有被释放,这时如果找不到问题所在,也可以重写控制器的返回按钮backBarButtonItem事件,在返回的时候进行移除通知操作2023-06-16 04:47:461
weak原理
weak原理 Runtime 维护了一个 weak表,用于存储指向某个对象的所有weak指针。weak表 其实是一个 hash(哈希)表,Key 是所指对象的地址,Value是 weak指针 的地址(这个地址的值是所指对象指针的地址)数组。 1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。 2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。 3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。 1.实现weak后,为什么对象释放后会自动为nil runtime对注册的类, 会进行布局,对于weak对象会放入一个hash表中。 用weak指向的对象内存地址作为key,当此对象的引用计数为0的时候会dealloc,假如weak指向的对象内存地址是a,那么就会以a为键, 在这个weak表中搜索,找到所有以a为键的weak对象,从而设置为nil。 2.当weak引用指向的对象被释放时,又是如何去处理weak指针的呢 1、调用objc_release 2、因为对象的引用计数为0,所以执行dealloc 3、在dealloc中,调用了_objc_rootDealloc函数 4、在_objc_rootDealloc中,调用了object_dispose函数 5、调用objc_destructInstance 6、最后调用objc_clear_deallocating,详细过程如下: a. 从weak表中获取废弃对象的地址为键值的记录 b. 将包含在记录中的所有附有 weak修饰符变量的地址,赋值为 nil c. 将weak表中该记录删除 d. 从引用计数表中删除废弃对象的地址为键值的记录 sideTable2023-06-16 04:47:531
循环引用
循环引用解释:循环引用可以简单理解为A引用了B,而B又引用了A,双方都同时保持对方的一个引用,导致任何时候引用计数都不为0,始终无法释放。若当前对象是一个ViewController,则在dismiss或者pop之后其dealloc无法被调用,在频繁的push或者present之后内存暴增,然后APP就duang地挂了。下面列举我们变成中比较容易碰到的三种循环引用的情形。 一方面,NSTimer经常会被作为某个类的成员变量,而NSTimer初始化时要指定self为target,容易造成循环引用。 另一方面,若timer一直处于validate的状态,则其引用计数将始终大于0。 block在copy时都会对block内部用到的对象进行强引用(ARC)或者retainCount增1(非ARC)。在ARC与非ARC环境下对block使用不当都会引起循环引用问题,一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是self.someBlock = ^(Type var){[self dosomething];或者self.otherVar = XXX;或者_otherVar = ...};block的这种循环引用会被编译器捕捉到并及时提醒 在委托问题上出现循环引用问题已经是老生常谈了,本文也不再细讲,规避该问题的杀手锏也是简单到哭,一字诀:声明delegate时请用assign(MRC)或者weak(ARC),千万别手贱玩一下retain或者strong,毕竟这基本逃不掉循环引用了! 可以运行循环引用的情况 单利的情况下是允许循环应用 不需要释放2023-06-16 04:48:011
arc dealloc什么时候调用
使用ARC后,系统会自动进行垃圾回收,所以dealloc就不用释放内存了,但是系统回首机制有一定的延迟性,如果项目较大或者对内存管理要求比较高,建议使用手动管理内存。使用ARC时通常使用alloc/init方法创建对象2023-06-16 04:48:081
iOS开发蓝牙缓存问题
单独写个类结束一个操作后销毁类 想要实现某项操作时重新alloc 记住要在dealloc中清除对蓝牙控制的类 不对 应该从kadvrlocalname取值 不应该从外设设备的name属性取(容易产生缓存)2023-06-16 04:48:271
NSNotificationCenter使用总结
通知中心对于iOS开发者最熟悉不过了,它实现了一对多的消息传递,可以实现跨页面传递。NSNotificationCenter的主要方法有以下几种: 其中最后一个方法返回 NSObserver 对象,在使用此方法添加观察者的时候,需要注意循环引用和 remove 的方式。每种方法在不同系统上的表现也不尽相同。 此方法中的 observer 是弱引用,直接使用self等不会造成强引用,此处提示一点,如果同一个通知连着添加n次,那SEL的方法会调用n次,所以要确保 addObserver 和 removeObserver 成对的出现。例如: 当发送 test 通知的时候,test 方法会被调用三次。 此方法需注意一下几点: 1) 这个方法会返回一个 NSObserver 对象,这个对象被系统强持有,调用者需要持有这个对象,用于停止通知移除观察者时使用。这种方式添加的通知,如果不持有这个对象,是无法彻底销毁这个通知的,具体做法如下: 2)这个方法中的 block 会强持有对象,所以在block中的成员变量和属性,需要进行弱引用,如果没有弱引用,则需要在 dealloc 函数之前对通知进行移除,否则不会执行 dealloc 函数。 发送通知主要是以下三种方式: 1、此处需要注意发送通知时,所在的线程,如果在子线程发送通知,那么通知的接收也是在子线程。 2、发送通知中的object参数要注意,这个和注册时候使用的方式有些关系,注册方法里,也有一个object参数,举个栗子: 上边两个注册通知的名字都是 lala ,唯一不同的是一个 object 是 nil ,一个是 _name。下面以两种不同的方式去发送通知: 第一种: 这种方式发送通知的时候,只有 test2 方法会被调用。 第二种: 这种方法发送通知的时候,test1 和 test2 都会被调用。如果此时我把 _name 改了会出现什么情况呢? 此时只有 test2 被调用,因为 _name 的地址被更改了,所以通知匹配不上了,导致 test1 无法被识别到了。以上可以说明如果添加通知的时候传入了 object 参数,那么发送通知时,就会匹配 name 和 object 两个条件。如果没传 object 参数,则只匹配 name。 突然发现移除方法里也有这个 object 参数, 为了思路的连贯,移除通知的方法写在这里。 按照上面的方法,添加了两个通知,一个携带 object参数,一个不携带 object参数,先按照以下操作移除: 此时的结果是 test1 方法通知被移除了,但是 test2 方法并没有被移除掉。如果在移除之前,修改了 _name 呢? 会发现此时效果是一样的,即使 _name 的地址发生了变化,但是 test1 的通知还是被移除了,test2 的通知没有被移除。如果按照以下方法移除: 此时 test1 和 test2 都被移除掉了。如果使用 效果是一样的,不同之处在于这个方法不止会移除掉自己添加的通知方法,同时也会移除掉系统的通知方法,所以除非是这个对象要被释放掉,不要轻易使用这种方法进行移除通知。 1、如果添加通知的时候传入了 object 参数,那么发送通知时,就会匹配 name 和 object 两个条件。如果没传 object 参数,则只匹配 name。 2、如果中途修改 object 对象,那么通过 [[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:_name]; 这种方式发送的通知将会失效。 3、移除通知的时候,如果remove中的 name 和 object 都存在, 那么以这种方式初始化的并且 name 一致,都会被移除掉。 如果 remove 中只有 name, 那么这个 name 下的所有通知都会被移除。 4、 [[NSNotificationCenter defaultCenter] removeObserver:self]; 不但会移除自己添加的通知,同时也会移除系统自动添加的通知,所以除非是类要被释放掉,不然谨慎使用这个方法。 在发送通知中已经进行了说明,移除通知主要有以下两种方法: 为 NSNotificationCenter 添加分类,hook 掉系统的移除通知方法 通过Log打印,我们会发现,在iOS8以上的系统,ViewController类在 dealloc 之后,自动调用 removeObserver 方法。 在 iOS9 系统之后,对象释放的时候,如果使用 SEL 方式进行添加,如果不移除通知,也不会有什么影响。如果 iOS9 系统之前,如果对象释放时候不进行移除,那么当对象释放之后,再发送通知,就有可能造成野指针的崩溃。所以还是建议进行移除。 如果使用 Block 的方式进行添加的,一定要持有对象,在释放的时候,自己手动进行释放操作,不然会出现对象释放了,但是发送通知的时候,block还是会被调用,此时 bolck 中的 self 对象已经是 nil 了。2023-06-16 04:48:341
移动端怎么实现可以上拉加载下拉刷新的同时,不会出现滚动条
拉刷新和下拉刷新的两种方法(包括使用第三方库MJRefresh)一、使用苹果原生的方法1、下拉刷新2、上拉刷新(1首先要新建一个footer得XIB文件,当然同时包括对应的控制器文件,例如在XIB文件中可以如下拖拉对应的控件(2然后在代码文件中写一个实例方法+(instancetype)footer{return[[[NSBundlemainBundle]loadNibNamed:@"XIB文件名"owner:niloptions:nil]lastObject];}(3然后在我们的列表控制器中调用:/***集成上拉刷新控件*/-(void)refreshUpStateDateList{XBLoadMoreFooter*footer=[XBLoadMoreFooterfooter];footer.hidden=YES;//一开始是要隐藏起来的,当scrollView拖拉到底部的时候我们才把它放出来self.tableView.tableFooterView=footer;//其实就是相当于把这个XIB文件当作是tableView的Footer}(4上拉到底部的时候去调用刷新数据的方法二、使用第三方库MJRefresh1、下拉刷新遵守协议:,然后其实就是把它当成tableview的header来用2、下拉刷新3、需要注意:最后需要dealloc2023-06-16 04:48:411
ios应用开发Objective-C笔试题
Objective-C笔试题是本文要介绍的内容,很详细的讲解写的答案。大约有18个Objective-C问题供你参考学习,不多说,我们一起来看详细解答!1.Objective-C中,与alloc语义相反的方法是dealloc还是release?与retain语义相反的方法是dealloc还是release,为什么?需要与alloc配对使用的方法是dealloc还是release,为什么?答:alloc与dealloc语意相反,alloc是创建变量,dealloc是释放变量。 retain 对应release,retain 保留一个对象。调用之后,变量的计数加1。或许不是很明显,在这有例为证:-(void)setName:(NSString*)name{ [nameretain]; [mynamerelease]; myname=name; }我们来解释一下:设想,用户在调用这个函数的时候,他注意了内存的管理,所以他小心的写了如下代码:NSString*newname=[[NSStringalloc]initWithString:@"John"]; [aClasssetName:newname]; [newnamerelease];我们来看一看newname的计数是怎么变化的。首先,它被alloc,count = 1; 然后,在setName中,它被retain, count = 2; 最后,用户自己释放newname,count = 1,myname指向了newname。这也解释了为什么需要调用[myname release]。我们需要在给myname赋新值的时候,释放掉以前老的变量。retain 之后直接dealloc对象计数器没有释放。alloc 需要与release配对使用,因为alloc 这个函数调用之后,变量的计数加1。所以在调用alloc 之后,一定要调用对应的release。另外,在release一个变量之后,他的值仍然有效,所以最好是后面紧接着再var = nil。2.在一个对象的方法里面:self.name = “object”;和name =”object”有什么不同吗?答:self.name = "object"会调用对象的setName()方法,name = "object"会直接把object赋值给当前对象的name 属性。[backcolor=transparent][backcolor=transparent]3.这段代码有什么问题吗:[backcolor=transparent]@implementationPerson [backcolor=transparent]-(void)setAge:(int)newAge{ [backcolor=transparent]self.age=newAge; [backcolor=transparent]} [backcolor=transparent] @end答:会进入死循环。4.什么是retain count?答:引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对 象的时候,不直接调用dealloc,而是调用release。release会 让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。5.以下每行代码执行后,person对象的retain count分别是多少Person*person=[[Personalloc]init];count1 [personretain];count2 [personrelease];count1 [personrelease];retaincount=1;6.为什么很多内置类如UITableViewController的delegate属性都是assign而不是retain的?答:会引起循环引用。7.定义属性时,什么情况使用copy,assign,和retain 。答:assign用于简单数据类型,如NSInteger,double,bool,retain 和copy用户对象,copy用于当 a指向一个对象,b也想指向同样的对象的时候,如果用assign,a如果释放,再调用b会crash,如果用copy 的方式,a和b各自有自己的内存,就可以解决这个问题。retain 会使计数器加一,也可以解决assign的问题。另外:atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:if(property!=newValue){ [propertyrelease]; property=[newValueretain]; }8.的对象是在什么时候被release的?答:autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。对于每一个Runloop,系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object(就是autorelease的对象)会被release。那什么是一个Runloop呢?一个UI事件,Timer call, delegate call, 都会是一个新的Runloop。那什么是一个Runloop呢?一个UI事件,Timer call, delegate call,都会是一个新的Runloop。9.这段代码有什么问题,如何修改for(inti=0;isomeLargeNumber;i++) { NSString*string=@”Abc”; string=[stringlowercaseString]; string=[stringstringByAppendingString:@"xyz"]; NSLog(@“%@”,string); }答:会内存泄露,for(inti=0;i1000;i++){ NSAutoreleasePool*pool1=[[NSAutoreleasePoolalloc]init]; NSString*string=@"Abc"; string=[stringlowercaseString]; string=[stringstringByAppendingString:@"xyz"]; NSLog(@"%@",string); [pool1drain]; }10.autorelease和垃圾回收机制(gc)有什么关系?答:不懂11.IPhone OS有没有垃圾回收(gc)?没有12.什么是Notification?答:观察者模式,controller向defaultNotificationCenter添加自己的notification,其他类注册这个notification就可以收到通知,这些类可以在收到通知时做自己的操作(多观察者默认随机顺序发通知给观察者们,而且每个观察者都要等当前的某个观察者的操作做完才能轮到他来操作,可以用NotificationQueue的方式安排观察者的反应顺序,也可以在添加观察者中设定反映时间,取消观察需要在viewDidUnload 跟dealloc中都要注销)。参考链接:13.什么时候用delegate,什么时候用Notification?答:delegate针对one-to-one关系,并且reciever可以返回值给sender,notification 可以针对one-to-one/many/none,reciever无法返回值给sender.所以,delegate用于sender希望接受到reciever的某个功能反馈值,notification用于通知多个object某个事件。14.什么是KVC和KVO?答:KVC(Key-Value-Coding)内部的实现:一个对象在调用setValue的时候,(1)首先根据方法名找到运行方法的时候所需要的环境参数。(2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。(3)再直接查找得来的具体的方法实现。KVO(Key-Value-Observing):当观察者为一个对象的属性进行了注册,被观察对象的isa指针被修改的时候,isa指针就会指向一个中间类,而不是真实的类。所以isa指针其实不需要指向实例对象真实的类。所以我们的程序最好不要依赖于isa指针。在调用类的方法的时候,最好要明确对象实例的类名。15.Notification和KVO有什么不同?答:不知道16.KVO在Objective-C中是怎么实现的?答:不知道17.ViewController 的 loadView, viewDidLoad, viewDidUnload 分别是在什么时候调用的?在自定义ViewController的时候这几个函数里面应该做什么工作?答:viewDidLoad在view 从nib文件初始化时调用,loadView在controller的view为nil时调用。此方法在编程实现view时调用,view 控制器默认会注册memory warning notification,当view controller的任何view 没有用的时候,viewDidUnload会被调用,在这里实现将retain 的view release,如果是retain的IBOutlet view 属性则不要在这里release,IBOutlet会负责release 。18.ViewController 的 didReceiveMemoryWarning 是在什么时候被调用的?默认的操作是什么?答:默认调用[super didReceiveMemoryWarning]小结:深度解析Objective-C笔试题的内容介绍完了,希望本文对你有所帮助!2023-06-16 04:48:551
swift中定时器(Timer) 的使用
Timer与NSTimer的使用注意: 1、由于ios是通过runLoop作为消息循环机制,主线程默认启动了runLoop,可是子线程没有默认的runLoop,因此在子线程启动runLoop即可。 2、runLoop的mode问题 如果timer是add到defaultRunLoopMode中,会出现滑动中UITrackingRunLoopMode得不到调度,导致定时器失效。 因为UITrackingRunLoopMode和KCFRunLoopDefaultMode都标记了common模式,所以可通过将timer添加到runLoopCommonModes实现在默认和追踪模式都能正常运行。 3、dealloc和定时结束时执行invalidate,置空timer。避免内存泄漏2023-06-16 04:49:021
WKWebView 中H5掉摄像头拦截照片进行处理,从新返回给H5方法
在 iOS 的 WKWebView 中,你可以通过以下步骤拦截摄像头拍摄的照片,并将处理后的结果返回给 H5:1. 在你的 iOS 应用程序中,确保已经配置了 WKWebView,并加载了相应的 H5 页面。2. 在你的代码中,实现 WKUIDelegate 的 `webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:)` 方法。这个方法会在 H5 页面调用 `alert()` 函数时触发。 ```swift func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { // 在这里判断是否是拦截照片的 alert if message == "拦截照片" { // 进行照片处理 let processedImage = processImage() // 自定义的照片处理方法 let encodedImage = encodeImage(processedImage) // 将图片编码为字符串,以便传递给 H5 // 返回处理后的结果给 H5 let js = "handleProcessedPhoto("(encodedImage)")" // 调用 H5 页面的处理方法,并传递处理后的照片数据 webView.evaluateJavaScript(js) { _, _ in completionHandler() // 完成回调 } } } ```3. 在你的 H5 页面中,你需要定义一个名为 `handleProcessedPhoto` 的 JavaScript 函数来处理接收到的照片数据。 ```javascript function handleProcessedPhoto(encodedImage) { // 在这里处理接收到的照片数据 // ... } ```通过以上步骤,你可以在 iOS 的 WKWebView 中拦截摄像头拍摄的照片,进行处理后将结果返回给 H5 页面进行进一步操作。请注意,在代码中的 `processImage()` 和 `encodeImage()` 方法需要根据你的具体需求和实现进行自定义。2023-06-16 04:49:092
怎么样删除UI组件
yileUI了解一下,自己跟域名上去,域名应该知道吧O(∩_∩)O。就是点什么什么的。2023-06-16 04:49:182
iOS使用openSSL加密应该怎么做
打开终端,进入项目根路径,执行这行命令就能执行了2023-06-16 04:49:262
CE修改游戏经验值,经验值超过所需经验值,但不升级,是什么原因?下面是一段代码,求高手解释。
.2023-06-16 04:49:352
iOS interactivePopGestureRecognizer卡住&手势滑动监听
手势侧滑导航在 iOS 中尤其常见,在项目中,经常和其打交道,但是经常发现,当侧滑到根控制器(第一级界面)时,继续快速使用侧滑手势 popGesture 会导致卡住。有时候,想要监听某个界面的侧滑手势是否结束来做一些其他操作,比如,在一个有 定时器(NSTimer) 的界面,使用侧滑到上一级页面,会导致该界面无法释放,这就需要知道当前侧滑手势是否已经结束,在结束时 移除(NSTimer) 等等。 滑动到首页(一般就是根控制器所属的第一级界面)会卡死, 这是iOS系统本身存在的一个BUG ,解决方案就是创建一个 BaseNavigationController (继承 UINavigationController ),在 BaseNavigationController 中监听 代理方法 ,判断当子视图控制器数 viewControllers 大于 1 时,允许 pop 手势可以滑动。在 BaseNavigationController.m 中,如下: 一般我们很少会需要监听侧滑到上一级的手势的情况,这种场景可能会出现在一些特殊处理中,比如当使用侧滑手势 pop 到上一级时,由于一些操作未完成,当前 self 被持有,而导致 dealloc 方法没有被执行,换句话说,当前 view controller 没有释放造成内存泄漏,这种情况下,监听侧滑手势是否执行就有必要了。在 BaseNavigationController.m 中,如下: 在 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated 中,可以使用 - (void)notifyWhenInteractionChangesUsingBlock: (void (^)(id <UIViewControllerTransitionCoordinatorContext>context))handler 监听到侧滑手势的情况,如果 context.isCancelled 为 YES ,说明用户侧滑的时候取消了滑动,即没有返回到上一级页面,反之,如果为 NO ,说明将 pop 到上一级页面,当前页面即将被释放。 因此,当 context.isCancelled 为 NO 时,我们可以发送一个通知( notification )到当前正在操作侧滑手势 pop 的这个页面,在这个页面做监听,就可以在收到通知时,执行你想要的操作就可以了。 比如,在 demo ( 文末有提供 )中,我们监听如下: 个人博客 Demo地址 2021-03-122023-06-16 04:50:121
如何在Swift里用UnsafeMutablePointer
如果你建一个Swift工程,当你创建一个C文件时,然后添加一些方法,比如返回char*类型方法(已经桥接完成),然后你在Swift中书写这个C定义的方法时,xcode会自动转换到swift中的类型。char*对应UnsafeMutablePointer,const char*是UnsafePointer。好了,我开个简单的头。看文章主要内容。大多数时候,C语言指针有两种方法导入到Swift中:UnsafePointer<T>UnsafeMutablePointer<T>这里的T是C类型的等价的Swift类型。声明为常量的指针被导入为UnsafePointer,非常量的指针则被导入为UnsafeMutablePoinger。这里有一些示例:C:void myFunction(const int *myConstIntPointer);Swift:func myFunction(myConstIntPointer: UnsafePointer<Int32>)C:void myOtherFunction(unsigned int *myUnsignedIntPointer);Swift:func myOtherFunction(myUnsignedIntPointer: UnsafeMutablePointer<UInt32>)C:void iTakeAVoidPointer(void *aVoidPointer);Swift:func iTakeAVoidPointer(aVoidPointer: UnsafeMutablePointer<Void>)如果不知道指针的类型,比如一个为前置声明的指针,则使用COpaquePointer。C:struct SomeThing;void iTakeAnOpaquePointer(struct SomeThing *someThing);Swift:func iTakeAnOpaquePointer(someThing: COpaquePointer)传递指针到Swift对象在很多情况下,传递指针到Swift对象和使用inout运算符一样简单,后者类似于C语言中的and运算符。Swift:let myInt: = 42myFunction(myInt)var myUnsignedInt: UInt = 7myOtherFunction(myUnsignedInt)这里有两个非常重要但容易被忽视的细节。当使用inout运算符时,使用var声明的变量和使用let声明的常量被分别转换到UnsafePointer和 UnsafeMutablePoinger,如果你不注意原来代码中的类型,就很容易出错。你可以试着向本来是UnsafeMutablePoinger 的地方传递一个UnsafePointer看看,编译器会报错。2. 这个运算符只在将Swift值和引用作为函数参数传递的上下文时生效,且该函数参数只接受UnsafePointer和UnsafeMutablePoinger两种类型。你不能在其它上下文获得这些指针。比如,下面的代码是无效的,并且会返回编译错误。Swift:let x = 42let y = x你可能会不时的需要交互操作一个API。来获取或返回一个空指针以代替显式类型,不幸的是这种做法在C语言里很普遍,导致无法指定一个通用类型。C:void takesAnObject(void *theObject);如果你确定函数需要获取什么类型的参数,你可以使用withUnsafePointer和unsafeBitCast将对象强制转换为空指针。比如,假设takesAnObject需要获取指向int的指针。var test = 42withUnsafePointer(test, { (ptr: UnsafePointer<Int>) -> Void invar voidPtr: UnsafePointer<Void> = unsafeBitCast(ptr, UnsafePointer<Void>.self)takesAnObject(voidPtr)})为了转换它,首先我们需要调用withUnsafeMutablePointer,这个通用函数包含两个参数。第一个参数是T类型的inout运算符,第二个是(UnsafePointer) -> ResultType的闭包。函数通过指向第一个参数的指针来调用闭包,然后然后将其作为闭包唯一的参数传递,最后函数返回闭包的结果。在上面的例子里, 闭包的类型被设置为Void,因此将不返回值。返回值的例子如下:let ret = withUnsafePointer(test, { (ptr: UnsafePointer<Int>) -> Int32 invar voidPtr: UnsafePointer<Void> = unsafeBitCast(ptr, UnsafePointer<Void>.self)return takesAnObjectAndReturnsAnInt(voidPtr)})println(ret)注意:你需要自己修改指针,通过withUnsafeMutablePointer变体来完成修改。为方便起见,Swift也包括传递两个指针的变体:var x: Int = 7var y: Double = 4withUnsafePointers(x, y, { (ptr1: UnsafePointer<Int>, ptr2: UnsafePointer<Double>) -> Void invar voidPtr1: UnsafePointer<Void> = unsafeBitCast(ptr1, UnsafePointer<Void>.self)var voidPtr2: UnsafePointer<Void> = unsafeBitCast(ptr2, UnsafePointer<Void>.self)takesTwoPointers(voidPtr1, voidPtr2)})关于unsafeBitCastunsafeBitCast 是一个极度危险的操作。文档将其描述为“将某物强制转换为和其他东西相同的长度”。在上面的代码我们能够安全的使用它的原因是,我们只是简单的转换不同类型的指针,并且这些指针的长度都是相同的。这也是我们为什么必须先调用withUnsafePointer来获取UnsafePointer,然后将其转换为UnsafePointer的原因。在一开始这可能会造成迷惑,特别是当处理与指针相同的类型时,比如Swift里的Int(在目前所有可用的,一个指针的长度是一个字符,Int的长度同样也是一个字符)。比如很容易就会犯下面的错误:var x: Int = 7let xPtr = unsafeBitCast(x, UnsafePointer<Void>.self)这段代码的意图是获取一个指针并传递给x。它会给人造成误解,尽管编译能通过并且能运行,但会导致一个意外错误。这是因为C API没有获取指针并传递给x,而是接收位于0x7或者其他地方的指针。因为unsafeBitCast要求类型的长度相等,所以当试图转换Int8或者一个字节的整型时没那么阴险了。var x: Int8 = 7let xPtr = unsafeBitCast(x, UnsafePointer<Void>.self)这段代码会简单的导致unsafeBitCast抛出异常和程序崩溃。与C语言中的结构体交互让 我们用实际的示例来展示这一部分。如果你想检索计算机所运行的系统信息,有一个C API:uname(2)可以达到目的。它接收指针指到一个数据结构,并且用系统信息填充所提供的对象,如OS名称和版本或者硬件识别符。但这里有一个问 题,导入到Swift的结构体是这样:struct utsname {var sysname: (Int8, Int8, 253 times, Int8)var nodename: (Int8, Int8, 253 times, Int8)var release: (Int8, Int8, 253 times, Int8)var version: (Int8, Int8, 253 times, Int8)var machine: (Int8, Int8, 253 times, Int8)}Swift将C中的数组字面量作为元组导入,并且默认的初始化程序要求每个字段都有值,所以如果你用Swift通常的做法来做的话,它将会变成:var name = utsname(sysname: (0, 0, 0, , 0), nodename: (0, 0, 0, , 0), etc)utsname(name)var machine = name.machineprintln(machine)这不是一个好方法。并且还存在另一个问题。因为utsname里的machine字段是元组,所以当使用println时将输出256位的Int8,但实际上只有字符串开始的几个ASCII值是我们需要的。那么,如何解决这个问题?Swift里的UnsafeMutablePointer提供两个方法,alloc(Int)和dealloc(Int),分别用来分配和解除分配模板T的数量参数。我们可以用这些API来简化我们的代码:let name = UnsafeMutablePointer<utsname>.alloc(1)uname(name)let machine = withUnsafePointer(name.memory.machine, { (ptr) -> String? inlet int8Ptr = unsafeBitCast(ptr, UnsafePointer<Int8>.self)return String.fromCString(int8Ptr)})name.dealloc(1)if let m = machine {println(m)}第一步是调用withUnsafePointer,将机器的元组传递给它,并通知它我们的闭包将返回一个附加字符串。在 闭包里面我们将指针转换为UnsafePointer,即该值的最等价的表述。除此之外,Swift的String包含一个类方法来初始化 UnsafePointer,这里的CChar是Int8类型的别名(typealias),所以我们能够将我们的新指针传递给初始化程序并且返回所需要 的信息。在获取withUnsafePointer的结果之后,我们能够测试它是否是let的条件声明,并打印出结果。对这个例子来说,它输出了期望的字段“x86_64”。总结最 后,说一下免责声明。在Swift中使用不安全的API应该被视为最后手段,因为它们是潜在不安全的。当我们转换遗留的C和Objective-C代码到 Swift中,有很大可能性我们会继续需要这些API来兼容现有工具。然而,当使用withUnsafePointer和unsafeBitCast作为 首选手段时应该始终抱有怀疑态度,并寻找其他更好的解决方案。新的代码应该尽可能符合语言习惯,不要在Swift代码中使用不安全的API。作为开发者,你应该了解如何使用你的工具以及什么地方该使用和什么地方不该使用。Swift将现代化带给了OS X和iOS开发,我们必须尊重它的理念。2023-06-16 04:50:261
请按照以下要求,编写一个程序实现判断字符串中是否存在png,如果有就删除它 oc
OC中内存管理机制应该就是引用计数的增减吧,retainCount为0时释放该内存。 retain对应的是release,内存的释放用release。 alloc对应的是dealloc,内存的销毁用dealloc。 readwrite此标记说明属性会被当成读写的,这也是默认属性。 readonly此标记说明属性只可以读,也就是不能设置,可以获取。 assign不会使引用计数加1,也就是直接赋值。 retain会使引用计数加1。 copy建立一个索引计数为1的对象,在赋值时使用传入值的一份拷贝。 nonatomic:非原子性访问,多线程并发访问会提高性能。 atomic:原子性访问。 strong:打开ARC时才会使用,相当于retain。 weak:打开ARC时才会使用,相当于assign,可以把对应的指针变量置为nil。2023-06-16 04:50:331
iOS 通知的3种使用方式
方法一 不传递参数, 最常用的一种 // 发送通知 -(void)btn1Click { [[NSNotificationCenter defaultCenter] postNotificationName:@"noti1" object:nil]; } //监听 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noti1) name:@"noti1" object:nil]; //调用方法 -(void)noti1{ NSLog(@"接收 不带参数的消息”); } 方法二 使用object 传递消息 -(void)btn2Click:(UIButton *)btn { [[NSNotificationCenter defaultCenter] postNotificationName:@"noti2" object:[NSString stringWithFormat:@"%@",btn.titleLabel.text]]; } //监听 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noti2:) name:@"noti2" object:nil]; //掉用方法 -(void)noti2:(NSNotification *)noti { //使用object处理消息 NSString *info = [noti object]; NSLog(@"接收 object传递的消息:%@",info); } 方法三 使用userInfo 传递消息 1。通知页面 2.接受处理页面 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeBgColor:) name:@"changeBgColor" object:nil]; -(void)dealloc{ NSLog(@"移除了所有的通知"); [[NSNotificationCenter defaultCenter] removeObserver:self]; //第二种方法.这里可以移除该控制器下名称为tongzhi的通知 //移除名称为tongzhi的那个通知 NSLog(@"移除了名称为tongzhi的通知"); [[NSNotificationCenter defaultCenter] removeObserver:self name:@"tongzhi" object:nil];2023-06-16 04:50:401
【OC梳理】循环引用及解决
循环引用是iOS开发中经常遇到的问题,它指的是两个或多个对象通过相互之间的强引用,形成了一个保留环,即使已经没有外部对象持有,也无法对其进行释放操作,也无法释放其占用的内存空间(引用计数器始终大于0)。 举个简单的例子: 对象A持有对象B,对象B持有对象C,对象C持有对象A,这时候它们之间就形成了一个引用环: 这时候如果有一个对象D,引用了对象A,那么由于ABC之间的循环引用,它们的引用计数器如下: 那么即使D释放了对象A,A、B、C的引用计数器仍然都是1,它们都不会被释放回收。 由于循环引用的存在,使得产生循环引用的对象始终占有内存空间,过多的循环引用会导致程序的内存占用不断升高,最终导致程序Creach。 用weak而不是strong就能解决这个问题了: Block的循环引用,主要是发生在ViewController中持有了block,比如: 同时在对callbackBlock进行赋值的时候又调用了ViewController的方法,比如: 就会发生循环引用,因为:ViewController->强引用了callback->强引用了ViewController,解决方法也很简单: 那是不是所有的block都会发生循环引用呢?其实不然,比如UIView的类方法Block动画,NSArray等的 类的遍历方法,都不会发生循环引用,因为当前控制器一般不会强引用一个类。 NSTimer是一种很容易忽略的循环引用的情况。 因为timer会强引用self,而self又持有了timer,这就造成了循环引用。 那么能不能像Block那样用一个weak指针解决呢?比如 但是其实并没有用,因为不管是weakSelf还是strongSelf,最终在NSTimer内部都会重新生成一个新的指针指向self,这是一个强引用的指针,结果就会导致循环引用。 如何解决呢?用NSProxy就是一个狠简便的方法 NSProxy本身是一个抽象类,它遵循NSObject协议,提供了消息转发的通用接口。NSProxy通常用来实现消息转发机制和惰性初始化资源。 使用NSProxy,你需要写一个子类继承它,然后需要实现init以及消息转发的相关方法: 创建NSTimer时,使用如下方法: 原理如下: 把虚线处变成了弱引用。于是,Controller就可以被释放掉,我们在Controller的dealloc中调用invalidate,就断掉了Runloop对Timer的引用,于是整个三个淡蓝色的就都被释放掉了。 参考文章: NSProxy与消息转发机制2023-06-16 04:50:461
wkwebview释放了但内存没减小怎么查
1、首先打开wkwebview这款软件,找到查询。2、其次找到查询后输入dealloc,在增加一个中间类去弱引用WKWebView。3、最后增加完后断开循环引用,点击查询即可查到wkwebview释放了但内存没减小的原因。2023-06-16 04:50:531
iOS闪退堆栈[UIKeyboardTaskEntry dealloc]和AutoreleasePoolPage::pop(void*)
之前版本中会有数组越界或者字典中插入nil导致闪退。上个版本就写了四个分类将系统的类 objectAtIndex: 和 objectAtIndexedSubscript: 等等方法交换一下进行安全校验。结果导致闪退bug增加很多。在此记录一下方便以后查找 当键盘显示的情况下 home app 进入后台,再单击app 图标 切换回前台时会出现crash。拿出iOS8的测试机尝试后果然能复现。查看闪退日志从iOS8-iOS10都有闪退 1.在iOS8上闪退堆栈信息 2.在iOS9上闪退堆栈信息 3.在iOS10上闪退堆栈信息 通过Build Phases中 -> Compile Sources 找到了这几个 Category改成mac: -fno-objc-arc 参考链接 1. https://shevakuilin.github.io/2018/08/14/%E4%BF%AE%E5%A4%8D%E4%B8%80%E4%B8%AA%E7%94%B1Hook%E7%B3%BB%E7%BB%9F%E7%B1%BB%E8%80%8C%E5%BC%95%E5%8F%91%E7%9A%84Crash/ 2. https://juejin.im/post/5b5746356fb9a04fd4509d5b2023-06-16 04:51:001
ios willdealloc和dealloc有什么关联
Objective-c 语言中最头疼的事就是内存释放,申明一个变量后记得一定要释放这个变量,在我的博客中已经有一些这方面的文章,我们定义的全局变量都是在 - (void)dealloc 函数中释放的;里面继承了一个[super dealloc]方法,有些同学平时自己释放内存都是写在 [super dealloc]的后面,但是在Objective-c 中不能这样写,所有的释放都必须写在 [super dealloc]的前面。-------错误的写法--------- (void)dealloc{[super dealloc];[XXX release];}-------正确的写法--------- (void)dealloc{[XXX release];[super dealloc];}原因是:“你所创建的每个类都是从父类,根类继承来的,有很多实例变量也会继承过来,这部分变量有时候会在你的程序内使用,它们不会自动释放内存,你需要调用父类的 dealloc方法来释放,然而在此之前你需要先把自己所写类中的变量内存先释放掉,否则就会造成你本类中的内存积压,造成泄漏”.不过在IOS6有了ARC后就不用手动去释放了,也没有此函数了!2023-06-16 04:51:181
如何查看和调试动态链接库的内存泄露
ios怎么查看内存泄露,有以下几种方法供大家参考:1.静态分析 通过静态分析我们可以最初步的了解到代码的一些不规范的地方或者是存在的内存泄漏,这是我们第一步对内存泄漏的检测。当然有一些警告并不是我们关心的可以略过。2.通过instruments来检查内存泄漏这个方法能粗略的定位我们在哪里发生了内存泄漏。方法是完成一个循环操作,如果内存增长为0就证明我们程序在该次循环操作中不存在内存泄漏,如果内存增长不为0那证明有可能存在内存泄漏,当然具体问题需要具体分析。3.代码测试内存泄漏在做这项工作之前我们要注意一下,在dealloc的方法中我们是否已经释放了该对象所拥有的所有对象。观察对象的生成和销毁是否配对。准确的说就是init(创建对象的方法)和dealloc是否会被成对触发(简单说来就是走一次创建对象就有走一次dealloc该对象)。下面是自己遇到的一些比较隐秘的造成内存泄漏的情况:1.两个对象互相拥有:也就是说对象a里面retain/addSubview了b对象,b对象同时也retain/addSubView了a对象。注意:delegate不要用retain属性,要用assign属性也会导致互相拥有。2.有时候需要用removeFromSuperView来释放:具体说明,也许我的a对象拥有一个b对象,b对象add到了c对象上,而在我们的设计中b对象的生命周期应该和a对象相同;这时候只一句[b release]/self.b = nil是不能把b对象释放掉的(一般情况下release会使其retainCount-1,[super dealloc]会再次将所有subView的retainCount-1,而b并不是a的subView,所有最后的一次-1没有了);所以我们需要在之前加上[b removeFromSuperView]。2023-06-16 04:51:321
ios检测到main函数泄露内存,怎么进一步定位
ios怎么查看内存泄露,有以下几种方法供大家参考:1.静态分析 通过静态分析我们可以最初步的了解到代码的一些不规范的地方或者是存在的内存泄漏,这是我们第一步对内存泄漏的检测。当然有一些警告并不是我们关心的可以略过。2.通过instruments来检查内存泄漏这个方法能粗略的定位我们在哪里发生了内存泄漏。方法是完成一个循环操作,如果内存增长为0就证明我们程序在该次循环操作中不存在内存泄漏,如果内存增长不为0那证明有可能存在内存泄漏,当然具体问题需要具体分析。3.代码测试内存泄漏在做这项工作之前我们要注意一下,在dealloc的方法中我们是否已经释放了该对象所拥有的所有对象。观察对象的生成和销毁是否配对。准确的说就是init(创建对象的方法)和dealloc是否会被成对触发(简单说来就是走一次创建对象就有走一次dealloc该对象)。下面是自己遇到的一些比较隐秘的造成内存泄漏的情况:1.两个对象互相拥有:也就是说对象a里面retain/addSubview了b对象,b对象同时也retain/addSubView了a对象。注意:delegate不要用retain属性,要用assign属性也会导致互相拥有。2.有时候需要用removeFromSuperView来释放:具体说明,也许我的a对象拥有一个b对象,b对象add到了c对象上,而在我们的设计中b对象的生命周期应该和a对象相同;这时候只一句[b release]/self.b = nil是不能把b对象释放掉的(一般情况下release会使其retainCount-1,[super dealloc]会再次将所有subView的retainCount-1,而b并不是a的subView,所有最后的一次-1没有了);所以我们需要在之前加上[b removeFromSuperView]。2023-06-16 04:51:501
ios怎么查看内存泄露
ios怎么查看内存泄露,有以下几种方法供大家参考:1.静态分析 通过静态分析我们可以最初步的了解到代码的一些不规范的地方或者是存在的内存泄漏,这是我们第一步对内存泄漏的检测。当然有一些警告并不是我们关心的可以略过。2.通过instruments来检查内存泄漏这个方法能粗略的定位我们在哪里发生了内存泄漏。方法是完成一个循环操作,如果内存增长为0就证明我们程序在该次循环操作中不存在内存泄漏,如果内存增长不为0那证明有可能存在内存泄漏,当然具体问题需要具体分析。3.代码测试内存泄漏在做这项工作之前我们要注意一下,在dealloc的方法中我们是否已经释放了该对象所拥有的所有对象。观察对象的生成和销毁是否配对。准确的说就是init(创建对象的方法)和dealloc是否会被成对触发(简单说来就是走一次创建对象就有走一次dealloc该对象)。下面是自己遇到的一些比较隐秘的造成内存泄漏的情况:1.两个对象互相拥有:也就是说对象a里面retain/addSubview了b对象,b对象同时也retain/addSubView了a对象。注意:delegate不要用retain属性,要用assign属性也会导致互相拥有。2.有时候需要用removeFromSuperView来释放:具体说明,也许我的a对象拥有一个b对象,b对象add到了c对象上,而在我们的设计中b对象的生命周期应该和a对象相同;这时候只一句[b release]/self.b = nil是不能把b对象释放掉的(一般情况下release会使其retainCount-1,[super dealloc]会再次将所有subView的retainCount-1,而b并不是a的subView,所有最后的一次-1没有了);所以我们需要在之前加上[b removeFromSuperView]。2023-06-16 04:51:581
自定义UIWindow怎么从内存中销毁
. 不调用-resignKeyWindow直接,它被重写以执行代码时,你UIWindows被删除。为了消除旧的窗口中,您需要创建一个UIWindow的新实例,并使其-makeKeyAndVisible,旧的窗口将辞去其关键在iOS 4的甚至会垃圾回收你的旧UIWindow中,只要你没有给它任何引用。在iOS中3.x中这样做会有效果。警告你。 2. 隐藏窗口的正确方法是设置hidden属性为YES。从的UIApplication的移除windows属性,您只要松开窗口(ARC您设置为nil的所有引用)。 当然,你会希望在这个拥有另一个窗口到位 3. 删除它是这样的:[myWindow resignKeyWindow];[myWindow release];释放使得它从窗户的UIApplication阵列得到消除。您可以查看[[的UIApplication sharedApplication]。窗口数],以验证它是否已正确删除。 4. 我有这个问题,它可能会有所帮助。 你需要摧毁所有强大的裁判删除之前的dealloc一个窗口,尤其是rootWindowController。我认为下面的代码,就足以删除任何窗口: [self.window.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; self.window.rootViewController = nil; [self.window resignKeyWindow]; [self.window removeFromSuperview];2023-06-16 04:52:051
『ios』根据runloop设计保活线程
为了避免频繁的创建线程,我们可以利用runloop来设计一个保活线程。 需要注意的点 run和runmode的区别 [NSRunLoop currentRunLoop] 调用run方法,永远都停不下来。 [NSRunLoop currentRunLoop] 调用runmode方法,会在一次循环之后停止。 子线程默认不会开启runloop 我们需要手动添加port来开启runloop 需要注意我们要设计的是一个子线程保活,当然关闭runloop的时候,也要关闭子线程的runloop [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES] 我们需要注意waitUntilDone这个参数,当设置为YES的时候,会等待performSelector执行完,才会执行后面的。如果设为NO则不会等待。 保活线程的销毁时机,是在调用dealloc的时候先进行停止在销毁。我们需要注意,在thread销毁过程中,while函数还在执行,要判断self是否还存在,防止直接调用self.stop造成的野指针错误2023-06-16 04:52:121
ios 修饰词作用
修饰词:assign、weak、strong、retain、copy、nonatomic、atomic、readonly、readwrite其中 ARC:assign、weak、strong、copy MRC:assign、retain、copy、nonatomic、atomic assign ( ARC/MRC )1.这个修饰词是直接赋值的意思 , 整型/浮点型等数据类型都用这个词修饰 . 2.如果没有使用 weak strong retain copy 修饰 , 那么默认就是使用 assign 了. ( 它们之间是有你没我的关系 ) 3.当然其实对象也可以用 assign 修饰 , 只是对象的计数器不会+1 . ( 与 strong 的区别 ) 4.如果用来修饰对象属性 , 那么当对象被销毁后指针是不会指向 nil 的 . 所以会出现野指针错误 . ( 与weak的区别 )weak ( ARC )(对象)1.弱指针是针对对象的修饰词 , 就是说它不能修饰基本数据类型(int float) . 2.weak 修饰的引用计数器不会+1 , 也就是直接赋值 . 3.弱引用是为打破循环引用而生的,比如在Block中,block在copy的时候,会对内部使用到的对象的引用技术+1,如果使用[self 方法名],那么就会有一个强指针指向self所在的class的内存地址,class的引用计数会+1,这样一来会导致class所在的内存地址无法被释放,造成内存泄漏 . 4.它最被人所喜欢的原因是 它所指向的对象如果被销毁 , 它会指向 nil . 从而不会出现野指针错误 .weak 和 assign的区别assign与weak,它们都是弱引用声明类型,但是他们是有区别的。 1.用weak声明的变量在栈中就会自动清空,赋值为nil。 2.用assign声明的变量在栈中可能不会自动赋值为nil,就会造成野指针错误!以delegate的声明为例,在MRC中多delegate声明使用的是assign,这是为了不造成循环引用,这时,我们需要在-dealloc方法中写上 self.delegate = nil,以免造成delegate的野指针错误。当然,在ARC中,只需要用weak声明delegate,就会自动释放了。strong ( ARC )(对象)1.直接赋值并且对象的引用计数器 +1 . 2.在 ARC 里替代了 retain 的作用 .retain ( MRC )1.release 旧对象( 旧对象计数器 -1 ) , retain 新对象( 新对象计数器 +1 ) , 然后指向新对象 . 2.在set方法里面是这样的 :if (_dog != nil) { [_dog release]; } _dog = [dog retain]; copy ( ARC/MRC )1.copy 在 MRC 时是这样做的 release 旧对象( 旧对象的引用计数器 -1 ) , copy 新对象( 新对象的引用计数器 +1 ) , 然后指向新对象 .(新对象是指最终指向的那个对象,不管深拷贝还是浅拷贝)1.1在set方法里面是这样的 : if (_dog != nil) { [_dog release]; } _dog = [dog copy]; 2.copy 在 ARC 时是这么干的 copy 新对象( 新对象的引用计数器 +1 ) , 然后指向新对象 .2.1在set方法里面是这样的 : _dog = [dog copy]; 3.使用注意 :3.1 修饰的属性本身要不可变的。例如 NSMutableArray 采用 copy 修饰 , 在addObject时会出现Crash, 因为NSMutableArray的对象在copy 过后就会变成NSArray。如果需要copy NSMutableArray对象,用:mutablecopy。 3.2 遵守 NSCopying 协议的对象使用 . nonatomic ( ARC/MRC )1.不对set方法加同步锁 . 2.性能好 3.线程不安全atomic ( ARC/MRC )1.原子属性就是对生成的 set 方法加互斥锁 @synchronized(锁对象) .@synchronized(self) { _delegate = delegate;} 2.需要消耗系统资源 . 3.互斥锁是利用线程同步实现的 , 意在保证同一时间只有一个线程调用 set 方法 . 4.其实还有 get 方法 , 要是同时 set 和 get 一起调用还是会有问题的 . 所以即使用了 atomic 修饰 还是不够安全 .nonatomic 和 atomic 的介绍和区别1. 什么是atomicity(原子性)?atomicity(原子性):我把原子性理解成线程对属性的单一执行。 例如,当两条线程同时执行一个属性的set方法的时候,如果不具有原子性(也就是声明属性时使用了nonatimic),那么就可能出现当A线程正在改写某属性值的时候,B线程也许会突然闯入,把尚未修改好的属性值读取出来。发生这种情况时,线程读取到的属性值肯能不对。2. 保证atomicity真的就线程安全了吗?为什么日常声明都用的是nonatomic呢?1.保证atomicity并非也是线程安全的,如果需要保证安全,需要跟深层次的线程锁定机制。 2.使用同步锁在iOS中开销比较大,会给程序带来性能上的问题。3. 为什么atomicity也不能保证线程安全?例如:当使用atomic时,仍然可能出现线程错误:当线程A进行set操作,这时其他线程的get或者set操作会因为等该操作而等待。当A线程的set操作结束后,B线程进行set操作,然后当A线程需要get操作时,却获得了在B线程的值,这就破坏了线程安全,如果有C线程在A线程get操作之前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还是要为线程添加lock来确保线程的安全。readonly (只读)1.让 Xcode 只生成get方法 . 2.不想把暴露的属性被人随便替换时 , 可以使用 .readwrite (读写)(默认)1.让 Xcode 生成get/set方法 . 2.不用 readonly 修饰时 , 默认就是 readwrite .2023-06-16 04:52:181
iOS代理了协议之后没法正常回调?
可以的是你没有把当前的控制器复制给代理2023-06-16 04:52:281
macOS 监听键盘输入的正确方式
macOS开发因为硬件上有鼠标和键盘的支持,所以我们也有监听硬件输入的需求。 监听键盘输入需要使用 NSEvent 这个类,这个类是专门管理“事件”的。 NSEvent 提供了两个监听的方法: 第一个方法是用于监听其他应用中的事件,第二个是用于监听自己应用的时间。更多详细内容可以阅读官方注释。 在这里我们使用第二个进行监听键盘输入: 在方法的 block 回调中会收到当前键盘的事件,你需要通过判断 aEvent.keyCode 来得知点击了哪一个键。macOS有一个枚举来告诉你键的名字是什么,你可以直接拿来判断(文末附)。 NSEvent 的监听是需要进行移除的 ,如果你不进行移除,那么这个需要监听的类再多次 init 后,其 block 也会在键盘点击时被重复的调用多次。 移除需要使用的类为 NSEvent ,方法为: 在 如何监听 那一节,添加监听的方法有一个返回值,这个返回值就是这里 removeMonitor 需要传入的参数。在添加了监听的对象 dealloc 或合适的时候进行调用即可。2023-06-16 04:52:451
mrc 和 arc分别在什么时候释放对象
页面消失的时候,在dealloc 中进行释放就行了,现在强制用 arc 了,不用 mrc了,不要纠结这些问题了2023-06-16 04:52:532
UITableView的原理
先前重新实现了一个list容器视图,由于Apple没有开源,在此分享过程中探索到的UITableView一些细节。 MPTableView: A list view like UITableView, more fast, more features. 1·捉摸不定的contentOffset UISrollview在滑动的时候,要获取其不断变化的contentOffset值,可通过其协议来获取也可以在其layoutSubviews里面获得,而后者所获取到的offset值会来得频繁很多——当快速滑动的时候,scrollView的协议回调次数远远低于layoutSubviews调用次数,也即contentOffset的获取次数更少,那样一旦需要根据contentOffset来做某些精确的工作的话,则效果会更差。 即使选择最容易被调用的layoutSubviews,其调用次数也并非都是线性变化的,layoutSubviews被回调的次数是有限(n次1s)的,所以一旦急速滑动,并不会逐像素回调,而是总的滑动距离内调用一定次数,最后 每次获得的contentOffset也将呈跳跃状 。 2·关于UIView的视图层次 发现在addSubview之后通过insertSubview: AtIndex:来设置子视图的层级后,一旦重新修改子视图的frame则这个index将失效。而后面发现了通过view.layer.zPosition的提升则可以令view在其父视图中一直处于高层级或者低层级。 这个是在实现plain模式下tableview的section header/footer停浮时候,需要解决的问题。因为section视图很多是比cell早加入显示区域的(header),那么当滚动到需要header浮在cell的头上时(遮住),则会出现cell遮住header的情况。一开始选择了zPosition,但是发现这个就破坏了视图的原始状态,后面直接bringToFront实现plain的section悬浮。 3·update相关 多路insert/delete/reload操作。 (1)最好把某种操作的IndexSet/Array全部整合成一个数组,这样最多就只有3个数组了 (2)这3种操作中reload是优先进行的,至于insert和delete,UIKit是让delete先进行,再进行insert,在这里面,reload其实就是做了先delete后insert操作。 (3)经常可以看见某些app进行reload某个cell来进行扩张其内容(cell的height变大)的效果,如果这个cell底部有分割线或者是其他内容的话,那么在这个扩张cell的过程中,动画效果是这个cell下面的cell往下移动,cell的高度被扩充,但是那个分割线却没有移动的效果而是直接出现在了最底部。这是由于我们通常选择了None的动画方式来进行reload,而None的动画方式其实就是单纯的视图hidden. (4)willInsertCell这个协议在insert动画之前是会被回调的,如果在insert操作里面选择了None的动画枚举,那么兴许可以通过这个协议做点自定义效果动画。这个不知道apple是否提供了这个机制。 4· c++类里面的NSObject成员会在类析构时自动dealloc掉,适用于STL容器。 5·UIScrollView的layoutSubviews调用时机 UIScrollView先进行setContentSize再进行addSubview操作,会执行layoutSubviews多次, 而如果把setContentSize操作放到最后,那么只会执行一次layoutSubviews 6·UITableView的cell重用限制 UITableView并未对重用cell的数量做限制,在测试过程中,被划出屏幕外的所有cell都没被销毁。这个测试过程中,将cell按顺序用height递增,即每个cell的高度越来越大,那么将会导致一个情况,越是滑动到后面,显示区域里面的cell会越少(相比之前滑动经过的区域来说),导致了进入重用的cell数量递增(因为前面一屏显示的cell数量会更多),那么这些 等待重用的cell虽然数量很大也不会被释放掉的 。 7·实用的hidden。 UITableView对滑出屏幕的cell都是进行hidden的而非remove,在设计cell重用缓存的时候,发现一旦频繁的进行cell的remove操作会比使用hidden的cpu使用率高上10%(在iPhone5上面),而UITableView好像早前版本是remove现在也改为hidden了。 在实际开发中,如果不是得销毁UIView,那么 hidden是比直接remove来得快 。另外设置hidden为YES会触发一次其子视图的removeFromSuperView操作。 8·xib和纯frame设置的问题 xib加载的视图再用frame进行设置,接着如果改变父视图的frame会导致其height变为0,那么就是因为xib加载的这个视图的默认autoresizingMask有height相关的,置为none即可。这个问题之前很困扰,一直没有发现这个猫腻,当然,只有在前面提到的这个特定条件下(手动改变视图frame,而其上面的子视图又是通过xib加载的)才会出现的。 9·cell相关。 (1)dequeueReusableCellWithIdentifier:identifier这个函数不调用并不能帮助你实现cell的不重用,仅需要在进行初始化的时候把reuseIdentifier赋值为nil即可,在这里initWithFrame创建的cell应该就是默认reuseIdentifier为nil了,因为UITableViewCell的NS_DESIGNATED_INITIALIZER不在initWithFrame。 (2)UITableViewCell被点选的触发操作,其实并非在cell内部实现手势点击,而是通过在UITableView里面通过获得当前点击的位置,来判断点击的是哪一个cell,接着调用cell的setSelected:animated函数的。这样做应该也是为了避免耦合吧。 (3)UITableViewCell的选中高亮。当选中cell的时候,你会发现cell上面的所有视图,都变成了clearColor来让cell的高亮背景色显示出来,而当取消cell高亮的时候又会变回去,那么这个问题出现了。这些子视图的背景色被动了!那么原本的背景色又被记录在哪里呢,需要复原的时候又能跑出来。 在这里有2个思路: ·继承UIColor来写一个类,提供一个属性,受记录的颜色,当setBackgroundColor的时候碰到的是这个class,就用他记录的颜色来设置颜色。每次要将所有子视图都设置为clearColor的时候,就将这些子视图的背景色都用这个类记录下来。 ·用一个容器存储这些视图对应的背景色,按键值对应,这里的key则选用每个UIView的内存地址。 ·UITableView使用了一个UIView的私有api来解决这个问题:-[UIView _descendent:willMoveFromSuperview:toSuperview:]。 很重要的一点是,当cell上面的willRemoveSubview被触发时,如果当前cell呈被选中高亮状态,他就会帮这个要被remove的子视图回复原有的背景色。 (4)UITableViewCell的layoutSubviews。一般来讲cell上面的那些默认元素,titleLabel和删除按钮左右滑动的,很多时候是没有用的。而一旦在UITableViewCell子类的layoutSubviews里面有super layoutSubviews,也会照顾到这些默认视图,实测中也会使cpu的占用率下滑几个%。 MPTableView 对UITableView的改进: ·提供了方法 - (void)reloadDataAsyncWithCompletion:(void(^)(void))completion 来进行异步的加载cell高度,选择了gcd的全局队列去计算和缓存高度完了再主线程刷新tableview——在这个过程中,如果tableview先前还有内容显示,那么在这个异步计算过程中,仍然可以滑动浏览原本的内容。 ·修复了update动画,特别针对UITableView的plain模式下,进行多路的insert/delete/reload操作之后,section的header/footer都会有很奇怪的运动轨迹(这个情况也发生在Mac OX下的NSTable组件)。同时提供了insert/delete等的自定义动画方法。 ·可以选择强制在reload的时候,继续重用之前产生的cell重用对象。UITableView的每次reload都会清除所有数据,可是很多时候,其实cell都是跟先前的一样,这样每次reload,无疑是多了删除旧的cell和创建新的过程——而实际上可以使用旧的。 ·多路的update操作支持,可以同时/延迟开启多个update事务,并且设定不同时间、动画状态。 ·同样的子视图样式下,cpu的占用率一定会比UITableView来得低。在同样的update操作下面,cpu的占用率也更低。还有cell拖动模式下也是。 ·以及其他的一些改进和新增api,可以在更低系统版本下使用新的api。2023-06-16 04:53:001
Swift报错 Cannot form weak reference to instance (0x15919e00) of class TestClass.
具体原因就是 lazy 和 weak 同时使用造成的 一般会出现在tableview的使用上,用lazy声明了tableview,然后tableview的delegate和datasource都是默认weak,在某些情况下就会造成崩溃。这是因为lazy的加载机制问题,如果在lazy的初始化方法中使用了delegate和DataSource,就好像是在dealloc中使用了weak对象一样,就会crash2023-06-16 04:53:071
ios开发,viewwilldisappear什么时候调用
就是试图将要消失的时候调用。比如你从控制器A跳转到控制器B,那么当控制器A的View快要消失时,就会调用Viewwilldisappear方法。顺便说下:1、 alloc 创建对象,分配空间2、init (initWithNibName) 初始化对象,初始化数据3、loadView 从nib载入视图 ,通常这一步不需要去干涉。除非你没有使用xib文件创建视图4、viewDidLoad 载入完成,可以进行自定义数据以及动态创建其他控件5、viewWillAppear 视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了6、viewDidAppear 视图已在屏幕上渲染完成当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反1、viewWillDisappear 视图将被从屏幕上移除之前执行2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放2023-06-16 04:53:281
swift 开发 怎么获得应用所占缓存
请参考下列 Swift 程序:// 查询当前任务基本信息let MACH_TASK_BASIC_INFO_COUNT = (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))// 准备参数let name = mach_task_self_let flavor = task_flavor_t(MACH_TASK_BASIC_INFO)var size = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)// 分配指针用于获取任务信息var infoPointer = UnsafeMutablePointer<mach_task_basic_info>.alloc(1)// 调用查询内存等状况的任务说明let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)// 获得信息指针let info = infoPointer.move()// 释放指针infoPointer.dealloc(1)// 检查返回值是否成功if kerr == KERN_SUCCESS { println("内存占用字节数:(info.resident_size)")} else { let errorString = String(CString: mach_error_string(kerr), encoding: NSASCIIStringEncoding) println(errorString ?? "出现错误,查询失败")}2023-06-16 04:53:351