I use ARC for my project
I have 1 class like this:
#implementation MyObject
+ (instancetype)shareInstance {
static id _shareInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_shareInstance = [[self alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(freeInstance)
name:kLC_Notification_FreeAllInstance object:nil];
});
return _shareInstance;
}
+ (void)freeInstance {
/*I want to release object "_shareInstance" but how??? */
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#end
But I can not release my instance object, so I have to change:
(move code line static id _shareInstance = nil; out of +shareInstance
#implementation MyObject
static id _shareInstance = nil;
+ (instancetype)shareInstance {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_shareInstance = [[self alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(freeInstance)
name:kLC_Notification_FreeAllInstance object:nil];
});
return _shareInstance;
}
+ (void)freeInstance {
_shareInstance = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#end
When I push notifcation with name:kLC_Notification_FreeAllInstance, all instance objects are release (all dealloc methods are both called). It's OK
BUT when I call it again....
all instances are not initialized on next call. And all instance objects will be nil after that
I made many breakpoint in block of dispatch_once and no breakpoint is called.
So my questions are:
Writing static id object;in a method and writing out of method, are they different?
How can I free all instance objects so I can still call them again?(I want to use ARC, I can do it without ARC)
I think you should set oncePredicate to 0 when releasing _shareInstance
#implementation MyObject
static id _shareInstance = nil;
static dispatch_once_t oncePredicate;
+ (instancetype)shareInstance {
dispatch_once(&oncePredicate, ^{
_shareInstance = [[self alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(freeInstance)
name:kLC_Notification_FreeAllInstance object:nil];
});
return _shareInstance;
}
+ (void)freeInstance {
_shareInstance = nil;
oncePredicate = 0;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#end
It works for me!
To answer your question in order...
The difference between writing the static variable inside and outside the method is scope, i.e. when writing the static inside the method it is only accessible from within that method.
This is a bit more tricky, as suggested by the name dispatch_once only runs the code in the block once, but I believe that it relies on the token/predicate to synchronize this, so moving this outside the shareInstance and setting to 0 should mean the the dispatch_once runs the block (once) the next time around
Related
Apple developer sample of Reachability cause Memory Leak when turning off wifi during play offline music (using this code) in my app and it got stuck line I mentioned bottom, It may cause by that Player code because log says [__NSCFString reachabilityDidChange:]: unrecognized selector
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject];
in this method:
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target, flags)
NSCAssert(info != NULL, #"info was NULL in ReachabilityCallback");
NSCAssert([(__bridge NSObject*) info isKindOfClass: [CDVReachability class]], #"info was wrong class in ReachabilityCallback");
CDVReachability* noteObject = (__bridge CDVReachability *)info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotificationString object: noteObject];
}
Here is log:
2015-04-12 19:52:03.416 HelloCordova[4094:1250289] -[__NSCFString reachabilityDidChange:]: unrecognized selector sent to instance 0x174232820
And reachabilityDidChange is in this file
#import "ReachabilityManager.h"
#import "Reachability.h"
#interface ReachabilityManager ()
#property NetworkStatus previousReachability;
#end
#implementation ReachabilityManager
- (id) init {
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityDidChange:)
name:kReachabilityChangedNotification
object:nil];
self.previousReachability = -1;
}
return self;
}
- (void) reachabilityDidChange:(NSNotification *)notification {
NSLog(#"reachabilityDidChange!");
CDVReachability * r = [notification object];
NetworkStatus ns = [r currentReachabilityStatus];
if (self.delegate && [self.delegate respondsToSelector:#selector(reachabilityDidChangeFrom:to:)]) {
[self.delegate reachabilityDidChangeFrom:self.previousReachability to:ns];
}
self.previousReachability = ns;
}
#end
I don't know is that correct but I try this:
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject];
});}
and even this:
// We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
// in case someon uses the Reachability object in a different thread.
#autoreleasepool {
Reachability* noteObject = (__bridge Reachability*)info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject];
}
I'm using a singleton to create a global socket.
When the app starts up the use is prompted to put in a code, which will be used in connecting to the host socket. How would I pass the data string from the view controller into the singleton to be used as the URL. This is how I have my singleton set up.
+ (SocketKeeperSingleton *) sharedInstance {
static dispatch_once_t _once;
static SocketKeeperSingleton *sharedSingleton = nil;
dispatch_once(&_once, ^{
sharedSingleton = [[SocketKeeperSingleton alloc] init];
[sharedSingleton initSIOSocket];
});
return sharedSingleton;
}
-(void) initSIOSocket {
[SIOSocket socketWithHost:#"http://localhost:8080" response:^(SIOSocket *socket) {
self.socket = socket;
[self.socket on:#"q_update_B" callback:^(NSArray *args) {
NSArray *tracks = [args objectAtIndex:0];
self.setListTracks = tracks;
[[NSNotificationCenter defaultCenter] postNotificationName:#"qUpdateB" object:nil];
}];
[self.socket on:#"current_artist_B" callback:^(NSArray *args) {
self.currentArtist = [args objectAtIndex:0];
[[NSNotificationCenter defaultCenter] postNotificationName:#"currentArtistB" object:nil];
}];
}];
}
You could simply refactor your singleton to something like this
+ (SocketKeeperSingleton *)sharedInstance {
static dispatch_once_t _once;
static SocketKeeperSingleton *sharedSingleton = nil;
dispatch_once(&_once, ^{
sharedSingleton = [[SocketKeeperSingleton alloc] init];
});
return sharedSingleton;
}
- (void)startSIOSocketWithHost:(NSString *)sHost {
[SIOSocket socketWithHost:sHost response:^(SIOSocket *socket) {
...
And then the first singleton usage after user input could be [[SocketKeeperSingleton sharedInstance] startSIOSocketWithHost:userInput]
Is there a best practice or library that helps cache processed images (i.e. images that have been created while the app is running) in iOS? I use SDWebImage for images that I download, but in various places in the app I blur or in other ways process some of these images. I would like to store the processed images in a cache so that I can access them easily rather than reprocess each time a user opens that image. What's the best way to do this?
Thanks!
The answer it seems is using NSCache. It's quite straightforward to do. I ended up with a subclass of NSCache to make sure memory warnings are handled.
Implementation of NATAutoPurgeCache (heavily based on other posts on StackOverflow)
#implementation NATAutoPurgeCache
+ (NATAutoPurgeCache *)sharedCache
{
static NATAutoPurgeCache *_sharedCache = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedCache = [[self alloc] init];
});
return _sharedCache;
}
- (id)init
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
#end
And using it when needed for an image: (in this case to store a blurred image)
UIImage* blurImage = [myCache objectForKey:#"blurred placeholder image"];
if (!blurImage)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage* blurImage = self.activityPic.image;
blurImage= [blurImage applyLightEffect];
dispatch_async(dispatch_get_main_queue(), ^{
self.activityPic.image = blurImage;
});
[myCache setObject:blurImage forKey:#"blurred placeholder image"];
});
}
else {
self.activityPic.image = blurImage;
}
I am trying to separate my GUI and my Logic...For example, i have one class which calculates something (calc.h/calc.m). And i have my GUI (viewController.h/viewController.m)...Now i want to update one Element of the GUI from another class... I already tried it with a Singleton, like this:
calc.m:
viewController *con = [viewController sharedInstance];
con.download.text = #"It Works";
viewController.m:
+ (id)sharedInstance {
static id sharedInstance;
#synchronized(self) {
if (!sharedInstance)
sharedInstance = [[viewController alloc] init];
return sharedInstance;
}
}
But it does´t work...
UPDATE:
in my ViewController:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"UpdateDownloadLabel"
object:nil];
- (void) receiveNotification:(NSNotification *) notification
{
NSDictionary* userInfo = notification.userInfo;
if ([[notification name] isEqualToString:#"Update"])
{
download.text = [userInfo valueForKey:#"Test"];
}
}
And in my other class i post the notification like this:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"UpdateDownloadLabel"
object:nil userInfo:info];
But it doesn't work...
That's not a proper use of a singleton. If you need to notify your UI from your model you have these options:
- delegation
- Local Notification
- KVO
The general rule is to keep your logic decoupled from your presentation, therefore your calc class shouldn't really know that a label named download exists.
Example: How to use notification.
In classA.h file
extern NSString * const ClassADidViewNotification;
In classA.m file
NSString * const ClassADidViewNotification = #"ClassADidViewNotification";
Register for notification in classA.m :-
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName: XXFooDidBarNotification
object:nil
queue:nil
usingBlock:^(NSNotification *notification)
{
NSLog(#"notification.object: %#", notification.object);
}
];
Post Notification:-
ClassB.m
[[NSNotificationCenter defaultCenter] postNotificationName: ClassADidViewNotification object:nil];
I try to send local notification. Here some code for class sending the notification:
#interface Sender : UIView
{
NSInteger itemID;
}
#implementation Sender
-(void) changedProperty
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationName" object:NULL];
}
And here the code to receive this notification:
#interface Listener : UIViewController
{
}
#implementation Listener
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectedItem:) name:#"NotificationName" object:NULL];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"NotificationName" object:NULL];
}
-(void) selectedItem:(NSNotification *) notification
{
// some actions
}
But this code doesn't work. Debugging I see how postNotificationName: object works but the method selectedItem: doesn't call
UPDATE.
Here is more code. Maybe this will help.
extern const NSString* selectItemNotificationName;
#interface vRoomSelectorItem : UIView
{
RoomSelectorItemBackground backgroundType;
NSInteger itemID;
}
#property NSInteger itemID;
-(void) setBackgroundType:(RoomSelectorItemBackground) backgroundType;
#interface vRoomSelectorItem ()
#property RoomSelectorItemBackground backgroundType;
#end
#implementation vRoomSelectorItem
const NSString* selectItemNotificationName = #"Reservation.SelectRoom";
-(RoomSelectorItemBackground) backgroundType
{
return backgroundType;
}
-(void) setBackgroundType:(RoomSelectorItemBackground)value
{
if(backgroundType != value)
{
backgroundType = value;
[self changedBackgroundType];
}
}
-(void) changedBackgroundType
{
if(backgroundType == RoomSelectorItemFilled)
{
// animation
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:(NSString*)selectItemNotificationName object:NULL userInfo:[[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInteger:itemID], #"ID", NULL]];
});
}
else
// reverse animation
}
#import "vRoomSelectorItem.h"
#interface vcReservationSelectRoom : UIViewController
{
NSMutableArray* arraySelectorItems;
}
#implementation vcReservationSelectRoom
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectedItem:) name:(NSString*)selectItemNotificationName object:NULL];
for(NSInteger i = 1; i <= SELECTOR_ITEM_COUNT; ++i)
{
vRoomSelectorItem* newItem = [[vRoomSelectorItem alloc] initWithFrame:CGRectMake(/*coordinates*/)];
[self.view addSubview:newItem];
[newItem setBackgroundType:RoomSelectorItemTransparent];
[newItem setItemID:i];
[arraySelectorItems addObject:newItem];
newItem = NULL;
}
}
-(void) selectedItem:(NSNotification *) notification
{
// some actions
}
-(void) dealloc
{
arraySelectorItems = NULL;
[[NSNotificationCenter defaultCenter] removeObserver:self name:(NSString*)selectItemNotificationName object:NULL];
}
#end
From your code in the quetion I think you can try this:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectedItem:) name:#"NotificationName" object:NULL];
[self.view changedProperty]; // method of your Sender class
}
It seems like your code should work. Make sure the notification is on the main thread and the workflow is as follows:
Add listener to notification center
Initiate sender
Send notification
You can be sure that it is on the main thread with:
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] post...];
});
Couple things I would change:
remove yourself from NSNotificationCenter in -(void)dealloc instead
of -(void)viewDidUnload. viewDidUnload will be deprecated, and
dealloc could get called without viewDidUnload.
For Notifications, I like to store the name in an external constant
to make sure that I don't typo the string:
//header.h
extern NSString *const notificationName;
//implementation.m
NSString *const notificationName = #"notification";
Ok, I've solved the problem. The piece of code above is not enough to understand, why it has not been working. The object of class vcReservationSelectRoom was temporary and it had been destroyed before sending notification from any vRoomSelectorItem. Sorry for my mistake of not showing enough code to solve this problem. And thank you all for helping.