Lifecycle of singleton instance after iOS tuned from background - ios

I have EventManager and Reg class singleton (Obj-C):
EventManager
class EventManager : NSObject {
override init() {
super.init()
Reg.shared().id = myId
Reg.shared().delegate = self
}
}
Reg (singleton)
#implementation Reg
//...
+(Reg*) shared{
static dispatch_once_t once;
static Reg *manager;
dispatch_once(&once, ^ { manager = [[Reg alloc] init]; });
return manager;
}
//...
#end
So here is my call in Controller:
class ViewController: UIViewController {
let manager = EventManager()
override func viewDidLoad() {
super.viewDidLoad()
let a = SomeHandler.instance
}
DispatchQueue.global(qos: .default).async {
SomeHandler.instance.registerBlocks({ obj in
let m = EventManager()
}, failureBlock: { (a, b, e) in
let m = EventManager()
}, status: { (a, b, c) in
}) { value in
let m = EventManager()
}
}
I get callback from SomeHandler.instance.registerBlocks sometimes after 10-15 sec when device entered to background and turned back to foreground
My question is: what happens with Reg instance?
If application is always active, each time when I call EventManager() I should get same instance of Reg because its singleton.
But when device enters to background OS deallocs all instances so when user opens app again Reg.shared() should return different instance, is it true?
What happens if old instance of Reg.shared() did some long job like sending HTTP requests?

To further elaborate on Paulw11's comment, please refer to the documentation about an app's life cycle.
A lot of people don't really specify what they mean with "background":
They just pressed the home button so the home screen or some other app is shown on the screen
The just put the device to sleep
They did either of this some time ago
They terminated the app with Xcode or restarted the device, seeing the app in the task manager (double pressing home button), so they assume it is in a background mode
Some other common unclear usages of "background" might also exist, but I think you'll get the picture.
As Paulw11 correctly said the only time your singleton will be deallocated is when the app enters the "not running" state, i.e. it is terminated. Note that this happens in case 4 I listed, but whether the app is listed in the task manager or not is not indicative of it running or not! I'm saying this since I have met people saying "my app just went to background, but when I put it to foreground again it appears all my on-start code is executed again!"
Case 3 can also ultimately result in your app being terminated (i.e. it goes from "suspended" to "not running"), but it needn't be so (depends on device usage and so forth).
The first two cases will result in the app at first entering background mode (your singleton is still there) and then suspended mode (the app is not doing anything anymore, but the memory is still intact, so your singleton won't be re-inited later).
In the end, a (true) singleton will only ever be deallocated when the app terminates, everything else would be grossly mis-using the term (also note that here lies the danger of singletons). The OS doesn't randomly go into your app's memory allocation and takes away its stuff. The only thing it does do is sending the memory warnings to let the app decide how to save memory on its own (if it's not suspended already). Only if even that does not "reign in" the app's resource usage memory is "freed": By killing it entirely.
#Paulw11: I didn't want to steal your answer, so why don't you make one from your comment. snaggs can then accept that. :)

Related

EXC_BAD_ACCESS when access != nil static var

Context: The app needs to be connected to a bluetooth device 100% of the time. While using the app, the CBPeripheral object is stored in a static variable for use throughout the user's session in app.
//This is in AppDelegate class.
static var connectedDevice: CBPeripheral? = nil
.........
class BluetoothScanner : NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
//(...)
func connectToDevice(peripheral: CBPeripheral)
{
AppDelegate.connectedDevice = peripheral
//(...)
}
}
At the start of the app, access operations to that variable occur in a loop. The first accesses to the variable are correct. But always, before ending the loop (at about 40 access to var), it happens EXC_BAD_ACCESS
if(AppDelegate.connectedDevice != nil)
{
//Thread 3: EXC_BAD_ACCESS (code=2, address=0x16ec8ff28)
print("Device debug: " + AppDelegate.connectedDevice!.debugDescription)
}
The problem occurs when calling 'debugDescription' but the same problem occurs when calling any other function of 'connectedDevice'
AppDelegate.connectedDevice is only accessed (in that period) it is never modified, nor released.
I have tested the app for two weeks very thoroughly and without problems. But when uploading it to the AppStore it indicated that I needed to update the XCode to the latest version in order to upload compilation to the AppStore. This problem has appeared without touching the code, only updating the XCode. I have even tested (before updating XCode) that this object is not released during hours of use of the app.

Like feedback using firebase crash. Fast click like-remove like

I have an application like instagram. It has feedback page.
When user likes some post, I add this like and feedback (with its own key (.childByAutoId) for this like.
static func add(_ newLike: LikeItem) {
// add like id for user feedback implementation
var like = newLike
let likeRef = ref.child("/userslikes/" + newLike.userId + "/onposts/" + newLike.postId).childByAutoId()
like.key = likeRef.key
var updates: [String: Any?] = [
"/userslikes/" + like.userId + "/onposts/" + like.postId: like.toAnyObject(),
"/postslikes/" + like.postId + "/" + like.userId: like.toAnyObject()
]
if like.userId != like.postAddedByUserId { // dont add your own likes
var likeForFeedBack = like.toAnyObject()
likeForFeedBack["isViewed"] = false // when user will open feedback -> true
updates.updateValue(likeForFeedBack, forKey: "/feedback/" + like.postAddedByUserId + "/" + like.key)
}
ref.updateChildValues(updates)
}
It's ok. And also I have remove function. It is going to like node, getting this like and feedbackId from this like. And then I make multi-part update.
static func remove(with userId: String, _ post: PostItem) {
var updates: [String: Any?] = [
"/userslikes/" + userId + "/onposts/" + post.key: nil,
"/postslikes/" + post.key + "/" + userId: nil
]
// deleting from feedback node
getLikeFromUser(id: userId, postId: post.key) { like in
if like.userId != like.postAddedByUserId {
updates.updateValue(nil, forKey: "/feedback/" + like.postAddedByUserId + "/" + like.key)
}
ref.updateChildValues(updates)
}
}
static func getLikeFromUser(id: String, postId: String,
completion: #escaping (_ likeId: LikeItem) -> Void) {
let refToLike = ref.child("/userslikes/" + id + "/onposts/" + postId)
refToLike.observeSingleEvent(of: .value, with: { snapshot in
let like = LikeItem(snapshot: snapshot)
completion(like)
})
}
So, when user taps "remove like" I have some delay (It is fetching like entity to get feedback id at this time).
And the problem: If I'm spamming like-removeLike button (like - remove like - l - rl - l - rl etc.), sometimes my feedback node is duplicating (with different keys ofc. It has not removed old node) and sometimes it is not adding (in this situation it is crashing if I try to remove it in the future).
How to fix it?
My humble opinion, first of all this could be fix with UX limitations. User shouldn't be able to spam any button in application. Must be a delay between this events. Even you can add some max. switch between user decisions... wait a while and make it free again (maybe).
Like you said on your comment, it's very good idea and good UX wait user until finish write operation. This way you can eliminate bad UX.
You can use userinteractionenabled property of UIView.
When set to NO, touch, press, keyboard, and focus events
intended for the view are ignored and removed from the event queue.
When set to YES, events are delivered to the view normally. The
default value of this property is YES.
During an animation, user
interactions are temporarily disabled for all views involved in the
animation, regardless of the value in this property. You can disable
this behavior by specifying the
UIViewAnimationOptionAllowUserInteraction option when configuring the
animation.
Of course there are many alternatives, sky is the limit for UX scenario.
Also you can check Apple's user interface guidelines for loading:
https://developer.apple.com/ios/human-interface-guidelines/interaction/loading/
Show content as soon as possible. Don’t make people wait for content
to load before seeing the screen they're expecting. Show the screen
immediately, and use placeholder text, graphics, or animations to
identify where content isn't available yet. Replace these placeholder
elements as the content loads. Whenever possible, preload upcoming
content in the background, such as while an animation is playing or
the user is navigating a level or menu.
and indicators maybe:
https://developer.apple.com/ios/human-interface-guidelines/ui-controls/progress-indicators/
If it’s helpful, provide useful information while waiting for a task
to complete. Include a label above an activity indicator to give extra
context. Avoid vague terms like loading or authenticating because they
don’t usually add any value.
Another option
Like you said in your comment below there is another option to keep like/dislike until user lives the ViewController. But there is another UX problem that when user try to close modal or back to previous view controller they will wait until this background job finish. Another issue if user kills the application you have 1 change left to save data and it's AppDelegate's applicationWillTerminate. But it's bad practice to save data there because 5 seconds limit:
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623111-applicationwillterminate
This method lets your app know that it is about to be terminated and
purged from memory entirely. You should use this method to perform any
final clean-up tasks for your app, such as freeing shared resources,
saving user data, and invalidating timers. Your implementation of this
method has approximately five seconds to perform any tasks and return.
If the method does not return before time expires, the system may kill
the process altogether. For apps that do not support background
execution or are linked against iOS 3.x or earlier, this method is
always called when the user quits the app. For apps that support
background execution, this method is generally not called when the
user quits the app because the app simply moves to the background in
that case. However, this method may be called in situations where the
app is running in the background (not suspended) and the system needs
to terminate it for some reason. After calling this method, the app
also posts a UIApplicationWillTerminate notification to give
interested objects a chance to respond to the transition.
Hope it helps.

Adobe Air Application - Not in background and not in Foreground - iPhone

I have an adobe air application - AS3 for iOs and Android.
Whenever the user clicks the home button, and thus the application is now in the background, the application automatically stops, which is the expected behavior. Now, if the user is in the application, and he double clicks his home button, showing all the multiple windows, the application continues running, which is not what i want. How can i access that state ( Not Background, not foreground )? If i can access it, i would then put my pausing code into that state, but how can i access that particular state?
When the user clicks the home button the app is moved to the background and suspended. The app isn't closed. The OS can close the app to free memory. If your app is a memory hog you'll see this happening.
You use events dispatched by the NativeApplication object. Below is example code to listen and handle these events.
import flash.events.Event;
import flash.desktop.NativeApplication;
import flash.desktop.SystemIdleMode;
// create listeners to NativeApplication
private var naApplication: NativeApplication;
naApplication = NativeApplication.nativeApplication;
naApplication.addEventListener(Event.ACTIVATE, eActivate);
naApplication.addEventListener(Event.DEACTIVATE, eDeactivate);
naApplication.addEventListener(Event.EXITING, eExiting);
private function eActivate(e: Event): void {
// app has opened or resumed
application.systemIdleMode = SystemIdleMode.KEEP_AWAKE;
}
private function eDeactivate(e: Event): void {
// app is going to be moved to background
application.systemIdleMode = SystemIdleMode.NORMAL;
}
private function eExiting(e: Event): void {
// app is going to be closed by user or by the OS (usually to free up memory)
// do whatever exit code here then remove all listeners (to be clean don't rely on OS to close them)
application.removeEventListener(Event.ACTIVATE, eActivate);
application.removeEventListener(Event.DEACTIVATE, eDeactivate);
application.removeEventListener(Event.EXITING, eExiting);
application.systemIdleMode = SystemIdleMode.NORMAL;
removeEventListener(Event.ENTER_FRAME, eMainTimer);
}
The systemIdleMode and ENTER_FRAME are just examples of typical code. Let me know of any questions.

Multi-threaded progress bar concurrency issue outside the main view controller

I've found so many solutions for progress bar update within the same thread and view controller, however they seemed to be not similar cases as mine.
In my application, the main view controller calls loadIntoCoreData()(implemented in class MyLoadingService) which asynchronously loads data into core data by another thread. This function has to continuously update the loading percentage (which is written in NSUserDefaults.standardUserDefaults()) to the main thread so that it could be shown on the progress bar in main view controller. I had ever used a while loop in MainViewController to continuously fetch the current percentage value, like below:
class MainViewController {
override func viewDidLoad() {
MyLoadingService.loadIntoCoreData() { result in
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "isLoading")
// do something to update the view
}
self.performSelectorInBackground("updateLoadingProgress", withObject: nil)
}
func updatingLoadingProgress() {
let prefs = NSUserDefaults.standardUserDefaults()
prefs.setBool(true, forKey: "isLoading")
// here I use a while loop to listen to the progress value
while(prefs.boolForKey("isLoading")) {
// update progress bar on main thread
self.performSelectorOnMainThread("showLoadingProcess", withObject: nil, waitUntilDone: true)
}
prefs.setValue(Float(0), forKey: "loadingProcess")
}
func showLoadingProcess() {
let prefs = NSUserDefaults.standardUserDefaults()
if let percentage = prefs.valueForKey("loadingProcess") {
self.progressView.setProgress(percentage.floatValue, animated: true)
}
}
}
And in the class of function loadIntoCoreData:
class MyLoadingService {
let context = (UIApplication.sharedApplication()delegate as! AppDelegate).managedObjectContext!
func loadIntoCoreData(source: [MyModel]) {
var counter = 0
for s in source {
//load into core data using the class context
NSOperationQueue.mainQueue.addOperationWithBlock({
// updating the value of "loadingProcess" in NSUserDefaults.standardUserDefaults()
// and synchronize it on main queue
})
counter++
}
}
}
The above code can successfully run the progress bar, however it often encounter BAD_ACCESS or some other exceptions(like "Cannot update object that was never inserted") due to the conflicts on core data context (thought it seems that managedObjectContext isn't touched by the main thread). Therefore, instead of using a while loop listening on the main thread, I consider using NSOperationQueue.performSelectorOnMainThread to acknowledge the main thread after each entry. Therefore I put my view controller as an argument sender into loadCoreData and call performSelectorOnMainThread("updateProgressBar", withObject: sender, waitUntilDone: true) but failed with error "unrecognized selector sent to class 'XXXXXXXX'". So I would like to ask if is it possible to update an UI object between threads? Or, how to modify my previous solution so that the core data context conflicts could be solved? Any solutions are appreciated.
class MyLoadingService {
func loadIntoCoreData(sender: MainViewController, source: [MyModel]) {
var counter = 0
for s in source {
//load into core data using the class context
NSOperationQueue.mainQueue.addOperationWithBlock({
// updating the value of "loadingProcess" in NSUserDefaults.standardUserDefaults()
// and synchronize it on main queue
})
NSOperationQueue.performSelectorOnMainThread("updateProgressBar", withObject: sender, waitUntilDone: true)
counter++
}
}
func updateProgressBar(sender: MainViewController) {
sender.progressView.setProgress(percentage, animated: true)
}
}
class MainViewController {
override func viewDidLoad() {
MyLoadingService.loadIntoCoreData(self) { result in
// do something to update the view
}
}
}
First, you are abusing NSUserDefaults in horrible ways. The documentation describes it as this...
The NSUserDefaults class provides a programmatic interface for
interacting with the defaults system. The defaults system allows an
application to customize its behavior to match a user’s preferences.
For example, you can allow users to determine what units of
measurement your application displays or how often documents are
automatically saved. Applications record such preferences by assigning
values to a set of parameters in a user’s defaults database. The
parameters are referred to as defaults since they’re commonly used to
determine an application’s default state at startup or the way it acts
by default.
You are using it to store a global variable.
Furthermore, you are completely abusing the user's CPU in your loop where you continuously are checking the value in the user defaults, and clipping off a selector to the main thread. "Abuse of the CPU" doesn't even come close to describing what this code is doing.
You should use NSProgress for reporting progress. There is a WWDC 2015 presentation dedicated exclusively to using NSProgress.
On to your core data usage.
Unfortunately, since you intentionally redacted all of the core data code, it's impossible to say what is going wrong.
However, based on what I see, you are probably trying to use that managed object context from your app delegate (which is probably still created with the deprecated confinement policy) from a background thread, which is a cardinal sin of the highest order as far as core data is concerned.
If you want to import data as a long running operation, use a private context, and execute the operations in the background. Use NSProgress to communicate progress to anyone wanting to listen.
EDIT
Thanks for the advice on my core data context usage. I digged into all
the contexts in my code and re-organized the contexts inside, the
conflict problem does not happen anymore. As for NSProgress , it's a
pity that the WWDC presentation focus on the feature on iOS 9 (while
my app must compact on iOS 8 devices). However, even though I use
NSProgress, I should still tell the main thread how many data the core
data (on another thread) already has, right? How does the thread on
NSProgress know the loading progress on my core data thread? –
whitney13625
You can still use NSProgress for iOS8, then only real difference is that you can't explicitly add children, but the implicit way still works, and that video explains it as well.
You really should watch the whole video and forget about the iOS9 part, except to know that you must add children implicitly instead of explicitly.
Also, this pre-iOS9 blog post should clear up any questions you have about it.

iOS. Save state when user exits an application?

For example:
- (void)someFunc {
[self someFunc1];
[self someFunc2];
[self someFunc3];
}
I call someFunc. As I understand if I interrupt the application then the application doesn't guarantee that all the inner code in someFunc will be performed.
I must call someFunc1, someFunc2 and someFunc3 only once.
The problems I don't know how to solve:
someFunc1, someFunc2 and someFunc3 should be called atomically.
storing info for next launch. For example if we successfully have performed someFunc1 only then at next launch the application should call someFunc2 and someFunc3 only.
I know about method applicationWillTerminate:, but I don't know how to solve the current issue with it.
EDITED
Multitasking is not a solution because Even if the device is running iOS 4 or later, the device may not support multitasking., so it doesn't solve the general problem and makes the final solution more difficult only.
EDITED
For those who spam with off topic answers: read the title first - Save state when user exits an application. Where have you seen here putting the application into background?
This does't make sense. If these functions are running on the main thread, there is no way that the application can terminate normally while your functions are running. This is because the events sent like applicationWillTerminate: are sent on the same thread.
If your function is running on a different thread to the main thread, you will need to save some state information after each function completes, but you still have a race condition.
It might be better to check your application's state before running each function. For example, if you have a three step login/registration process with a server, you should query the server to see if the stage has been completed already before running it.
It's difficult to be more specific without knowing what you are doing in these functions.
You should use background tasks !
Take a look at the documentation here :
Executing a Finite-Length Task in the Background
Put the call of someFunc in the middle of the background task.
If your app goes to the background state, you'll have extra time to finish the execution of the method.
Make your functions to return bool, and when you call them, store the bool value to nsdefaults.
When the app restarts,check the bools from sndefaults, and if they are NO, run the functions and update them.
Nobody wants to help. So my temporary solution:
to save a last state I use a writing to a file because it enables to set its operation as atomic/nonatomic
I have replaced this code with something like this:
typedef enum {
state1,
state2,
state3
} MyState;
#property (assign) MyState state;
-(void)someFunc {
switch (state) {
case state1:
{
[self someFunc1];
state = state2;
[self someFunc];
break;
}
case state2:
{
[self someFunc2];
state = state3;
[self someFunc];
break;
}
default:
break;
}
}

Resources