iOS 13 has a new way of sending app lifecycle events:
#implementation SceneDelegate
- (void)sceneDidDisconnect:(UIScene *)scene {
}
- (void)sceneDidBecomeActive:(UIScene *)scene {
}
- (void)sceneWillResignActive:(UIScene *)scene {
}
- (void)sceneWillEnterForeground:(UIScene *)scene {
}
- (void)sceneDidEnterBackground:(UIScene *)scene {
}
#end
SWIZZLE code:
#implementation UIScene (SWIZZLE)
- (void)my_setDelegate:(id <UISceneDelegate>)delegate {
// do custom work
[self my_setDelegate:delegate];
}
+ (void)load {
[self swizzle:#selector(setDelegate:) with:#selector(my_setDelegate:)];
}
#end
typedef IMP *IMPPointer;
BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store) {
IMP imp = NULL;
Method method = class_getInstanceMethod(class, original);
if (method) {
const char *type = method_getTypeEncoding(method);
imp = class_replaceMethod(class, original, replacement, type);
if (!imp) {
imp = method_getImplementation(method);
}
}
if (imp && store) { *store = imp; }
return (imp != NULL);
}
#implementation NSObject (RuntimeAdditions)
+ (BOOL)swizzle:(SEL)original with:(IMP)replacement store:(IMPPointer)store {
return class_swizzleMethodAndStore(self, original, replacement, store);
}
#end
Document:
#pragma mark - Delegate
// UIScene is strongly retained by UIKit like UIApplication, however, unlike UIApplication, the delegate may not need to live for the whole lifetime of the process.
// A strong ref here relieves clients of the responsibility of managing the delegate lifetime directly.
#property (nullable, nonatomic, strong) id<UISceneDelegate> delegate;
that is no't work ,how to do swizzle UIWindowScene delegate ?
Related
I have one class MyOldController with init method
-(instancetype) initWithMyController: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
I want swizzle this initialization method to another and this my swizzle code
#implementation MyOldController(Swizzle)
+ (void)load {
[MyOldController swizzleMethods];
}
+ (void)swizzleMethods {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(initWithMyController)), class_getInstanceMethod(self, #selector(swizzle_ initWithMyController)));
}
I try write this
-(instancetype) swizzle_initWithMyController: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
But it drops error
Then I renamed init method to this and updated (void)swizzleMethods
-(instancetype) initWithMyController_swizzle: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
Error message disappeared but swizzle doesn't works. It just calls old initialization method, not my new.
Which point i missed? Is swizzling of initialization method have some special way to do it?
(Starting with the required caveat: this is incredibly dangerous and should never be used in production code. Swizzling initializers is particularly dangerous given designated initializer chaining, and should definitely never be done for anything but exploration and debugging without first confirming the implementation of the swizzled initializer. OK, got that out of the way.)
I can't reproduce your issue. And initializer should always start with with init, so your second approach is correct. I suspect you've just made a small mistake, perhaps in your #selector (which has a typo in your question, which suggests maybe there's a mistake in your actual code). Here is code that does what you're describing.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface MyOldController: NSObject
- (instancetype)initWithInt:(NSInteger)x
#end
#implementation MyOldController
- (instancetype)initWithInt:(NSInteger)x
{
self = [super init];
if (self) {
NSLog(#"init");
}
return self;
}
#end
#implementation MyOldController(Swizzle)
+ (void)load {
[MyOldController swizzleMethods];
}
+ (void)swizzleMethods {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(initWithInt:)), class_getInstanceMethod(self, #selector(initWithInt_swizzle:)));
}
- (instancetype)initWithInt_swizzle:(NSInteger)x
{
self = [super init];
if (self) {
NSLog(#"init_swizzle");
}
return self;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyOldController *controller = [[MyOldController alloc] initWithInt:1];
NSLog(#"%#", controller);
}
return 0;
}
This prints, as expected:
2018-06-21 12:23:14.431936-0400 test[30981:401466] init_swizzle
2018-06-21 12:23:14.432172-0400 test[30981:401466] <MyOldController: 0x10051ee10>
As a foray into new programming languages, I build well known data structures to familiarize myself with the syntax and the basic ins & outs of the language. In this case, I examine the stack in Objective-C. From Apple's Working with Objects we read about the keyword 'id'
...This is a special keyword used in Objective-C to mean “some kind of object.” It is a pointer to an object, like (NSObject *), but is special in that it doesn’t use an asterisk.
By using the keyword 'id', it seems possible to create a stack data structure that holds differing types of Obj-C objects; however, I am not sure if this as intended. Is it better to create the various class methods for each potential data type rather than attempting a generic method and make sure each stack adheres to a single Object type?. Here is what I have so far
XYZNode.h
#import <Foundation/Foundation.h>
#interface XYZNode : NSObject
#property id value;
#property XYZNode *next;
-(instancetype)initWithValue:(id)aValue next:(XYZNode *)aNext;
-(instancetype)init;
// Class factory methods should always start with the name of
// the class (without the prefix) that they create, with the
// exception of subclasses of classes with existing factory methods.
+(XYZNode *)nodeWithValue:(id)aValue nextNode:(XYZNode *)aNext;
#end
XYZNode.m
#import "XYZNode.h"
#implementation XYZNode
-(instancetype)initWithValue:(id)aValue next:(XYZNode *)aNext {
if (self = [super init]) {
_value = aValue;
_next = aNext;
} return self;
}
-(instancetype)init {
return [self initWithValue:nil next:nil];
}
+(XYZNode *)nodeWithValue:(id)aValue nextNode:(XYZNode *)aNext {
return [[self alloc] initWithValue:aValue next:aNext];
}
#end
XYZStack.h
#import <Foundation/Foundation.h>
#interface XYZStack : NSObject
-(void)pushValue:(id)aValue;
-(id)popValue;
-(BOOL)isEmpty;
-(instancetype)init;
-(instancetype)initWithValue:(id)aValue;
+(XYZStack *)stackWithValue:(id)aValue;
#end
XYZStack.m
#import "XYZStack.h"
#import "XYZNode.h"
// The extension hides how the values are stored
#interface XYZStack ()
#property XYZNode *lastNodeAdded;
#end
#implementation XYZStack
// Default initializer
-(instancetype)initWithValue:(id)aValue {
if (self = [super init]) {
_lastNodeAdded = nil;
}
if (aValue) {
[self pushValue:aValue];
}
return self;
}
// Call default initializer
-(instancetype)init{
return [self initWithValue:nil];
}
-(BOOL)isEmpty{
return ([self lastNodeAdded] == nil);
}
-(void)pushValue:(id)aValue {
[self setLastNodeAdded:[XYZNode nodeWithValue:aValue nextNode:[self lastNodeAdded]]];
}
-(id)popValue {
id temp = [[self lastNodeAdded] value];
[self setLastNodeAdded:[[self lastNodeAdded] next]];
return temp;
}
+(XYZStack *)stackWithValue:(id)aValue {
return [[self alloc] initWithValue:aValue];
}
#end
Any comments would be appreciated.
I was reading this article by Jeff Kelley and trying to do the same. However the code was written before ARC was adopted and now fails to compile.
http://blog.slaunchaman.com/2011/04/11/fun-with-the-objective-c-runtime-run-code-at-deallocation-of-any-object/
The main problem is in this part of the printout, some casting errors and then blocked release messages. I found it to be a very interesting example but I can't seem to get it to work.
The problems are:
0. Autosynthesized property 'block' will use synthesized instance variable '_block', not existing instance variable 'block' on the #implementation JKBlockExecutor
1. Cast of block pointer type 'voidBlock' (aka 'void (^)(void)') to C pointer type 'const void *' requires a bridged cast and Cast of C pointer type 'void *' to block pointer type 'typeof (aBlock)' (aka 'void (^__strong)(void)') requires a bridged cast" on the block = Block_copy(aBlock); line
2. Cast of block pointer type 'voidBlock' (aka 'void (^)(void)') to C pointer type 'const void *' requires a bridged cast on Block_release(block);
typedef void (^voidBlock)(void);
#interface JKBlockExecutor : NSObject {
voidBlock block;
}
#property (nonatomic, readwrite, copy) voidBlock block;
- (id)initWithBlock:(voidBlock)block;
#end
#implementation JKBlockExecutor
#synthesize block;
- (id)initWithBlock:(voidBlock)aBlock
{
self = [super init];
if (self) {
block = Block_copy(aBlock);
}
return self;
}
- (void)dealloc
{
if (block != nil) {
block();
Block_release(block);
}
[super dealloc];
}
#end
This is where he creates a category on NSObject.
const void *runAtDeallocBlockKey = &runAtDeallocBlockKey;
#interface NSObject (JK_RunAtDealloc)
- (void)runAtDealloc:(voidBlock)block;
#end
#implementation NSObject (JK_RunAtDealloc)
- (void)runAtDealloc:(voidBlock)block
{
if (block) {
JKBlockExecutor *executor = [[JKBlockExecutor alloc] initWithBlock:block];
objc_setAssociatedObject(self,
runAtDeallocBlockKey,
executor,
OBJC_ASSOCIATION_RETAIN);
[executor release];
}
}
#end
This is how you execute the example.
NSObject *foo = [[NSObject alloc] init];
[foo runAtDealloc:^{
NSLog(#"Deallocating foo!");
}];
[foo release];
Or another way to get other information.
NSObject *foo = [[NSObject alloc] init];
__block id objectRef = foo;
[foo runAtDealloc:^{
NSLog(#"Deallocating foo at address %p!", objectRef);
}];
[foo release];
Can this code be fixed somehow? I took out all the release messages to no avail.
Code below builds and works (or at least seems so), and prints "Deallocating foo!" when I expect it to print it. Part 1:
typedef void (^voidBlock)(void);
#interface JKBlockExecutor : NSObject {
voidBlock block;
}
#property (nonatomic, readwrite, copy) voidBlock block;
- (id)initWithBlock:(voidBlock)block;
#end
#implementation JKBlockExecutor
#synthesize block = block;
- (id)initWithBlock:(voidBlock)aBlock
{
self = [super init];
if (self) {
block = [aBlock copy];
}
return self;
}
- (void)dealloc
{
if (block != nil) {
block();
block = nil;
}
}
#end
Part 2:
const void *runAtDeallocBlockKey = &runAtDeallocBlockKey;
#interface NSObject (JK_RunAtDealloc)
- (void)runAtDealloc:(voidBlock)block;
#end
#implementation NSObject (JK_RunAtDealloc)
- (void)runAtDealloc:(voidBlock)block
{
if (block) {
JKBlockExecutor *executor = [[JKBlockExecutor alloc] initWithBlock:block];
objc_setAssociatedObject(self,
runAtDeallocBlockKey,
executor,
OBJC_ASSOCIATION_RETAIN);
}
}
#end
Testing if it works:
#autoreleasepool {
NSObject *foo = [[NSObject alloc] init];
[foo runAtDealloc:^{
NSLog(#"Deallocating foo!");
}];
}
EDIT
Changed Block_release(block); to block = nil;
If you want to know more about the code below ,please go to Fun With the Objective-C Runtime: Run Code at Deallocation of Any Object
Part 1:create one class: the object We Want To Be Released When That Happens--- this class is like an event:when the target obj dealloc,it happens。use block to execute the event 。
// .m file
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// the object We Want To Be Released When That Happens--- this class is like an event:when the target obj dealloc,it happens。use block to execute the event 。
typedef void (^voidBlock)(void);
#interface CYLBlockExecutor : NSObject
- (id)initWithBlock:(voidBlock)block;
#end
// .m file
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// the object We Want To Be Released When That Happens--- this class is like an event:when the target obj dealloc,it happens。use block to execute the event 。
#import "CYLBlockExecutor.h"
#interface CYLBlockExecutor() {
voidBlock _block;
}
#implementation CYLBlockExecutor
- (id)initWithBlock:(voidBlock)aBlock
{
self = [super init];
if (self) {
_block = [aBlock copy];
}
return self;
}
- (void)dealloc
{
_block ? _block() : nil;
}
#end
Part 2:core code:use runtime to realize cyl_runAtDealloc method
// CYLNSObject+RunAtDealloc.h file
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// use runtime to realize cyl_runAtDealloc method
#import "CYLBlockExecutor.h"
const void *runAtDeallocBlockKey = &runAtDeallocBlockKey;
#interface NSObject (CYLRunAtDealloc)
- (void)cyl_runAtDealloc:(voidBlock)block;
#end
// CYLNSObject+RunAtDealloc.m file
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
// use runtime to realize cyl_runAtDealloc method
#import "CYLNSObject+RunAtDealloc.h"
#import "CYLBlockExecutor.h"
#implementation NSObject (CYLRunAtDealloc)
- (void)cyl_runAtDealloc:(voidBlock)block
{
if (block) {
CYLBlockExecutor *executor = [[CYLBlockExecutor alloc] initWithBlock:block];
objc_setAssociatedObject(self,
runAtDeallocBlockKey,
executor,
OBJC_ASSOCIATION_RETAIN);
}
}
#end
How to use :
#import "CYLNSObject+RunAtDealloc.h"
then
NSObject *foo = [[NSObject alloc] init];
[foo cyl_runAtDealloc:^{
NSLog(#"Deallocating foo!");
}];
Does anyone know if there is a way to set a property like a string in the User Defined Runtime Atributes sections of Interface Builder without creating a subclass of said component? For example, I want to store a metadata value for each component in my interface that I use later. I just don't want to have to create a subclass or each component to add a metadata property.
This is one approach I came up with. Opinions?
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#interface UIControl(MetaData)
#property (nonatomic, retain) id entityProperty;
#end
#implementation UIControl(MetaData)
static char const * const EntityPropertyKey = "EntityProperty";
#dynamic entityProperty;
- (id)entityProperty {
return objc_getAssociatedObject(self, EntityPropertyKey);
}
- (void)setEntityProperty:(id)newEntityProperty {
objc_setAssociatedObject(self, EntityPropertyKey, newEntityProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
...
if (textField.entityProperty)
[managedObject setValue: textField.text forKey:textField.entityProperty];
You could keep an NSDictionary somewhere, perhaps in a singleton object that has methods for issuing unique ids for objects and storing metadata by the id keys in the dictionary. The UI objects have a tag property that you can use, if your ids are just incremented integers. Then the dictionary keys would just be NSNumbers for those unique integers.
Like this:
#import <Foundation/Foundation.h>
#interface ACLMetadataManager : NSArray
+(ACLMetadataManager*) sharedMetadataManager;
-(NSUInteger) getUniqueId;
-(void) setObject: (id) object forId:(NSUInteger) theId;
-(id) objectForId:(NSUInteger) theId;
#end
And:
#import "ACLMetadataManager.h"
#implementation ACLMetadataManager { // Private variables
NSMutableDictionary *_metadata;
NSUInteger _ids;
}
- (id)init {
self = [super init];
if (self) {
_metadata = [[NSMutableDictionary alloc] init];
}
return self;
}
+(ACLMetadataManager*) sharedMetadataManager { // Singleton getter
static ACLMetadataManager *instance;
if (instance != nil) {
return instance;
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
static dispatch_once_t oneTimeThread;
dispatch_once(&oneTimeThread, ^(void) {
instance = [[ACLMetadataManager alloc] init];
});
#else
#synchronized(self) {
instance = [[ACLMetadataManager alloc] init];
}
#endif
return instance;
}
-(NSUInteger) getUniqueId { // Increment unique id when getter is called.
return ++_ids; // Start from 1 because tag is 0 by default.
}
-(void) setObject: (id) object forId:(NSUInteger) theId {
[_metadata setObject:object forKey:[NSNumber numberWithInteger:theId]];
}
-(id) objectForId:(NSUInteger) theId {
return [_metadata objectForKey:[NSNumber numberWithInteger:theId]];
}
// Override some methods to ensure singleton stays instantiated.
- (id) retain {
return self;
}
- (oneway void) release {
// Does nothing here.
}
- (id) autorelease {
return self;
}
- (NSUInteger) retainCount {
return INT32_MAX;
}
#end
Usage:
ACLMetadataManager *metadataManager = [ACLMetadataManager sharedMetadataManager];
myControl.tag = [metadataManager getUniqueId];
[metadataManager setObject:myMetadata forId:myControl.tag];
I have some very small classes that I feel should be "pulled up" but the methods are so small I'm not sure. For example, the only thing that's meaningfully different is the body of the buildFromJSON: selector.
I acknowledge that this is similar to:
Pull-up refactoring, Objective-C
but I feel my question is specific to refactoring very small classes/methods.
Also, not sure it relates to my particular code example, but I'm wondering if a child class says it conforms to a protocol, whether it's enough that it's parent actually supply the implementation of required selector(s)?
#implementation AsyncFoo
-(void)dealloc {
[clientDelegate release];
[super dealloc];
}
- (id)initWithDelegate: (id <ServiceClientProtocol>) delegate {
if((self = [super init])) {
clientDelegate = [delegate retain];
}
return self;
}
- (void)buildFromJSON:(NSString*)jsonResponseString {
[clientDelegate serviceComplete:[RestAdapter buildFooArray: jsonResponseString]];
}
#end
#implementation AsyncBar
-(void)dealloc {
[clientDelegate release];
[super dealloc];
}
- (id)initWithDelegate: (id <ServiceClientProtocol>) delegate {
if((self = [super init])) {
clientDelegate = [delegate retain];
}
return self;
}
- (void)buildFromJSON:(NSString*)jsonResponseString {
[clientDelegate serviceComplete:[RestAdapter buildBarArray:jsonResponseString]];
}
#end
Answers including code example would be great.
EDIT: Post accepted answer I'd like to add that since I was able to subclass, the derived classes did not need to declare that they conformed to protocol:
#interface Async : NSObject <ModelBuilderProtocol> {
id <ServiceClientProtocol> clientDelegate;
}
- (void)buildFromJSON:(NSString*)jsonResponseString;
#end
#interface AsyncArtistById : Async
#end
You don't normally retain your delegates as this can cause a retain cycle.
Knowing what I know from looking at your example I would probably implement like this:
The super class
// Async.h
#interface Async : NSObject
#property (nonatomic, assign) id<ServiceClientProtocol> delegate;
- (void)buildFromJSON:(NSString *)jsonResponseString;
#end
// Async.m
#implementation Async
#synthesize delegate = _delegate;
- (id)initWithDelegate:(id<ServiceClientProtocol>)delegate
{
self = [super init];
if(self) {
_delegate = delegate;
}
return self;
}
- (void)buildFromJSON:(NSString *)jsonResponseString
{
// This will ensure that we over ride this method in a sub class
[NSException raise:NSInternalInconsistencyException
format:#"You must override %# in a subclass", NSStringFromSelector(_cmd)];
}
#end
Concrete subclass AsyncFoo
// AsyncFoo.h
#interface AsyncFoo : Async
#end
// AsyncFoo.m
#implementation AsyncFoo
- (void)buildFromJSON:(NSString *)jsonResponseString
{
[self.delegate serviceComplete:[RestAdapter buildFooArray: jsonResponseString]];
}
#end
Concrete subclass AsyncBar
// AsyncBar.h
#interface AsyncBar : Async
#end
// AsyncBar.m
#implementation AsyncBar
- (void)buildFromJSON:(NSString *)jsonResponseString {
[self.delegate serviceComplete:[RestAdapter buildBarArray:jsonResponseString]];
}
#end