I haven't written anything for several months. Today I have time to write something about IOs (I found myself very unfocused, and I didn't do a good job in Android, and I switched to iOS again ).
In my program, the function of getting the user's current location address is provided. I wrote an auxiliary locationhelper class for positioning. In this class, cllocationmanager is called to take over the didupdatetolocation event to obtain the longitude and latitude coordinates, and then send a coordinate request to the background to return the address. In use, I create a locationhelper class in a viewcontroller and pass the viewcontroller as the lochandler's delegate to the past. When the coordinates are obtained, the positioning function is immediately stopped and the background request is sent to the return address, after obtaining the address, call back the lochandler method to complete the address Locating Process. In this process, the program will display a positioning progress bar that does not allow user operations until the positioning is complete to obtain the address. Locationhelper will be maintained by viewcontroller until the viewcontroller is released.
The approximate class definition of locationhelper is as follows:
@ Implementation locationhelper @ synthesize lochandler;-(ID) initlocationhelper :( id <mylocationdelegate>) handler {self = [Super init]; self. lochandler = handler; locationman = [[cllocationmanager alloc] init]; locationman. delegate = self; [locationman startupdatinglocation]; return self;}-(void) locationmanager :( cllocationmanager *) manager didupdatetolocation :( cllocation *) newlocation fromlocation :( cllocation *) oldlocation {// obtain the latitude and longitude cllocationcoordinate2d coordinate = newlocation. coordinate; cllocationdegrees latitude = coordinate. latitude; cllocationdegrees longpolling = coordinate. longpolling; [locationman stopupdatinglocation]; geoaddresshelper * gah = [[geoaddresshelper alloc] initwithgeox: longpolling andgeoy: latitude andresultdelegate: Self]; self. curgah = gah; [gah release];}-(void) ongeoaddressfound :( nsobject *) RES {[lochandler locationhelperfoundaddress: res];} @ end
Geoaddresshelper converts the longitude and latitude to a Chinese address through a network request:
@implementation GeoAddressHelper@synthesize eventDelegate,geoX,geoY;-(void)initWithGeoX:(double)x andGeoY:(double)y andResultDelegate:(NSObject*)evtDlg{ self.eventDelegate=evtDlg; geoX=x; geoY=y; NSMutableString * url=(NSMutableString*)[MyApp getServerHttpUrl:@"opId=7100017"]; [url appendFormat:@"&x=%.6f&y=%.6f",x,y]; NetReqOperation * req=[[NetReqOperation alloc] initWithURL:url withDelegate:self]; [[MyApp netReqQueue] addOperation:req]; [req release];}- (void)OnNetReqFinished:(NSObject *)res{ [eventDelegate performSelectorOnMainThread:@selector(OnGeoAddressFound:) withObject:res waitUntilDone:YES];}@end
However, for the two seemingly simple classes, memory address errors (unrecognizable selector and so on) may occur from time to time, leading to program crash. The error stack is as follows:
-[__NSCFSet OnGeoAddressFound:]: unrecognized selector sent to instance 0xf678640(null)(0 CoreFoundation 0x340848d7 __exceptionPreprocess + 1861 libobjc.A.dylib 0x342d41e5 objc_exception_throw + 322 CoreFoundation 0x34087acb -[NSObject doesNotRecognizeSelector:] + 1743 CoreFoundation 0x34086945 ___forwarding___ + 3004 CoreFoundation 0x33fe1680 _CF_forwarding_prep_0 + 485 Foundation 0x359ce1b7 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 2666 Foundation 0x359cde49 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 1367 AutoTraffic2012 0x001232db -[GeoAddressHelper OnNetReqFinished:] + 5028 Foundation 0x359ce1b7 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 2669 Foundation 0x359cde49 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 13610 AutoTraffic2012 0x0011170f -[NetReqOperation OnNetReqFinished:] + 10611 CoreFoundation 0x33fe322b -[NSObject performSelector:withObject:] + 4212 Foundation 0x35a6e757 __NSThreadPerformPerform + 35013 CoreFoundation 0x34058b03 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 1414 CoreFoundation 0x340582cf __CFRunLoopDoSources0 + 21415 CoreFoundation 0x34057075 __CFRunLoopRun + 65216 CoreFoundation 0x33fda4dd CFRunLoopRunSpecific + 30017 CoreFoundation 0x33fda3a5 CFRunLoopRunInMode + 10418 GraphicsServices 0x3085efcd GSEventRunModal + 15619 UIKit 0x3745b743 UIApplicationMain + 1090)
From the error stack, it is clear that locationhelper is released. However, because locationhelper is referenced by viewcontroller and viewcontroller is displaying a positioning progress bar during this period, operations cannot exit. Therefore, it is theoretically impossible to be released in advance. This error is not easy to reproduce. I have been studying the code for a long time and have not found out where the writing is wrong.
After a lot of tests, we found that there was such a rule: if the program is often used, this error is not easy to appear; if the operation is slow, it is not easy to make a mistake; but if you leave your phone idle for a while, in addition, the interface switching speed is faster, so this error is very likely to occur during the first use.
After a period of idle time, what is the difference between program usage and normal usage? I think it may be related to the first positioning. When operating on a map, a rough location is often displayed for the first time, and a more precise location is displayed later. This may trigger two didupdatetolocation events consecutively, resulting in errors; the subsequent positioning may be a single positioning. In the Code, when the first position is successful, I have immediately called stopupdatinglocation to stop the position scan. Theoretically, the position will not be triggered again, so I have never thought about it.
However, the actual situation seems to be different from the imagination. From the symptom, it is likely that didupdatetolocation is triggered twice. So I added a locfired ID to the locationhelper. Once triggered, the ID is set to yes immediately and will not be processed again. After such processing, the error disappears.
Originally, stopupdatinglocation does not necessarily stop positioning immediately. After I obtained the coordinates of longitude and latitude for the first time and completed the address query, the positioning progress bar on viewcontroller disappears and allowed the operation. At this time, the locatipnhelper did not completely stop scanning, the didupdatetolocation event was triggered again, and a request for coordinates was initiated; at this time, viewcontroller may immediately be closed and released by pop when the user is fast, and locationhelper is also released, resulting in a memory access error caused by geoaddresshelper callback locationhelper when the request is complete. The problem is solved by setting a flag to prevent the second trigger.
Some people will say that, since geoaddresshelper references locationhelper, why not increase the reference count of locationhelper to prevent it from being automatically released? In fact, I did the same for the first time, but this caused another problem, so I did not add a reference later. I will explain the details in the next article.