Setter and Getter for variable using objc/runtime.h - objective-c

I have good Setter and Getter code for Object. How do same for BOOL using objc/runtime.h like here?
objc_getAssociatedObject require object
.h
#import <Foundation/Foundation.h>
#interface UITableView (Additions)
#property (nonatomic, retain) NSNumber *allowReplenishment;
#end
.m
#import "UITableView+Additions.h"
#import <objc/runtime.h>
#implementation UITableView (Additions)
- (NSNumber *)allowReplenishment {
return objc_getAssociatedObject(self, #selector(allowReplenishment));
}
- (void)setAllowReplenishment:(NSNumber *)value {
objc_setAssociatedObject(self, #selector(allowReplenishment), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end

If you want to declare your bool property
#property (nonatomic, assign) BOOL isAllowReplenishment, just create the object in the setter instead, as needed:
- (void)setIsAllowReplenishment:(BOOL)isAllowReplenishment
{
NSNumber *isBool = [NSNumber numberWithBool:isAllowReplenishment];
objc_setAssociatedObject(self, #selector(isAllowReplenishment), isBool, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isAllowReplenishment
{
NSNumber *isBool = objc_getAssociatedObject(self, #selector(isAllowReplenishment));
return isBool.boolValue;
}

Related

Swift: "Mapper is not a type", using custom mapping class for arrays in swift and Objective C

I have an 2 objective C classes:
Class 1 scanDatabase (Scans a database and puts that into a mutable array)
Class 2 Mapper (Is a mapping class for the database scan "model")
In objective C this successfully scans the database and puts it into a mutable array. Using the mapping class I can access individual groups of elements (AlbumTitles) like so:
for (Mapper *mapper in scanResult) {
NSLog(#"%#", mapper.AlbumTitle);
}
Everything is working as it should and I can return individual elements from my array i.e as above I am only returning album titles.
I then need to use that array in Swift. I call the objective C in my Swift class and again it runs fine and creates the array. This is done with:
let scanTable = ScanTable();
let scanMapper = Mapper();
scanTable.scanTableDo();
but when I try to retrieve a particular set of items like Album title as I did in the objective C for loop above I get the error "scanMapper is not a type" (scanMapper is my swift instance of the objective C mapper class:
I tried two different ways and both have the same error:
for mapper: scanMapper in scanTable.scanResult {
print("\(mapper.AlbumTitle)")
}
for object in scanTable.scanResult as! [scanMapper] {
print("\(mapper.AlbumTitle)")
}
Can I use an objective C class as a model/mapper and not sure whether I would need to recreate it in Swift.
I will include the mapper and scanTable .h and .m code just in case it is needed, plus the bridging header:
Mapper.h:
#import <Foundation/Foundation.h>
#import <AWSDynamoDB/AWSDynamoDB.h>
#interface Mapper : AWSDynamoDBObjectModel <AWSDynamoDBModeling>
#property (nonatomic, strong) NSNumber *SongID;
#property (nonatomic, strong) NSString *Artist;
#property (nonatomic, strong) NSString *SongURL;
#property (nonatomic, strong) NSString *Location;
#property (nonatomic, strong) NSNumber *UserRatings;
#property (nonatomic, strong) NSNumber *AVGUserRating;
#property (nonatomic, strong) NSString *Category;
#property (nonatomic, strong) NSString *PictureURL;
#property (nonatomic, strong) NSNumber *SongDuration;
#property (nonatomic, strong) NSString *SongTitle;
#property (nonatomic, strong) NSNumber *AVGMusicianRating;
#property (nonatomic, strong) NSString *AlbumTitle;
#end
Mapper.m
#import <AWSDynamoDB/AWSDynamoDB.h>
#import "Mapper.h"
#implementation Mapper
+ (NSString *)dynamoDBTableName {
return #"Songs";
}
+ (NSString *)hashKeyAttribute {
return #"SongID";
}
#end
ScanTable.h:
#import <Foundation/Foundation.h>
#import <AWSDynamoDB/AWSDynamoDB.h>
#interface ScanTable : NSObject
- (void) scanTableDo;
#property (nonatomic, strong) NSMutableArray *scanResult;
#end
ScanTable.m
#import "ScanTable.h"
#import "Mapper.h"
#implementation ScanTable
- (void) scanTableDo {
AWSDynamoDBObjectMapper *dynamoDBObjectMapper = [AWSDynamoDBObjectMapper defaultDynamoDBObjectMapper];
AWSDynamoDBScanExpression *scanExpression = [AWSDynamoDBScanExpression new];
scanExpression.limit = #10;
[[dynamoDBObjectMapper scan:[Mapper class]
expression:scanExpression]
continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"The request failed. Error: [%#]", task.error);
}
if (task.exception) {
NSLog(#"The request failed. Exception: [%#]", task.exception);
}
if (task.result) {
AWSDynamoDBPaginatedOutput *paginatedOutput = task.result;
NSMutableArray *scanResult = [[NSMutableArray alloc] initWithArray:paginatedOutput.items]; //// ADDED /////
for (Mapper *mapper in scanResult) {
NSLog(#"%#", mapper.AlbumTitle);
}
}
return nil;
}];
}
#end
//EDITED ADDED BRIDGING HEADER//
MySampleApp-Bridging-Header.h:
//
// MySampleApp-Bridging-Header.h
// MySampleApp
#import "ScanTable.h"
#import "Mapper.h"
#import "Hello World.h"
Thanks for your help
The problem is just as the error explains, you're attempting to cast the items in your array to scanMapper, which is a variable holding an instance of Mapper, not the Mapper type itself. Assuming that scanTable.scanResult is an NSArray of Mappers, try this instead:
guard let scanResult = scanTable.scanResult as? [Mapper] else {
print("scanResult was not an array of mappers!")
return
}
for mapper: Mapper in scanResult {
print("\(mapper.AlbumTitle)")
}

ARC property qualifier

I'm relatively new to ARC. I'm making an UIView subclass, that will have two labels (title and subtitle). I don't want to publicly expose the labels as properties, only their text.
I'm currently using this:
#interface MyView : UIView
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *subtitle;
#end
 
#implementation MyView
{
UILabel *_titleLabel;
UILabel *_subtitleLabel;
}
- (void)setTitle:(NSString *)title
{
[_titleLabel setText:title];
}
- (NSString *)title
{
return [_titleLabel text];
}
- (void)setSubtitle:(NSString *)subtitle
{
[_subtitleLabel setText:title];
}
- (NSString *)subtitle
{
return [_subtitleLabel text];
}
#end
Are my two #properties correctly declared? Should I use the strong, weak or any other qualifier? And why?
If you are going to work with setter / getter, I think the appropiate tag would be the readwrite. strong weak retain etc apply when the property is the setter/getter for an instance variable.

In Objective-C, how do I pass a property as an argument for a function and then call the setter/getter methods?

The code is probably the best way to see what I am trying to do:
AcInfo.h:
#interface AcInfo : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString *registrationNumber;
#end
AcInfo.m:
#implementation AcInfo
#dynamic registrationNumber;
#end
AnotherClass.h:
#interface AnotherClass : NSObject {
}
#property (nonatomic, retain) AcInfo *detailItem;
#property (nonatomic, retain) IBOutlet UITextField *registrationNumberTextField;
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget;
#end
AnotherClass.m:
#import "AcInfo.h"
#implementation AnotherClass
#synthesize detailItem, registrationNumberTextField;
- (void)viewDidLoad
{
[super viewDidLoad];
registrationNumberTextField.text = #"Test";
// I expect this to set detailItem.registrationNumber to the value of
// registrationNumberTextField.text (Test) but it doesn't change anything!
setDetailItemValueFromUIElement:registrationNumberTextField forAcInfoTarget:detailItem.registrationNumber;
}
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget
{
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
// This doesn't do anything when it returns!
(NSString *)acInfoTarget = (UITextField *)uiElement.text
return;
}
}
#end
In short, I want acInfoTarget to call the getter [detailObject registrationNumber] and the setter [detailObject setRegistrationNumber] in the setDetailItemValueFromUIElement: function...
You can set or read properties by name using
// setter
NSString *propertyName = #"myProperty";
[object setValue:newValue forKey:propertyName];
// getter
id value = [object valueForKey:propertyName];
This is slower than using the normal dot notation, though, and it's frequently (though not always) a sign of poorly-designed code.
Also note that id is a pointer type, so you probably don't actually mean "(id*)".
Your code wants to look something like this, I think:
- (void)setDetailItemValueFromUIElement:(id)uiElement forAcInfoTarget:(NSString*)acInfoTarget {
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
NSString *newValue = ((UITextField*)uiElement).text;
[self.detailItem setValue:newValue forKey:acInfoTarget];
}
}
Properties are just syntax sugar for a couple of accessor methods. They are not, in essence, variables so you shouldn't treat them as such. If you want to affect a property, then what you wanting to do is call a method. So you should pass a id and selector parameter and not pointer to a variable type.

Passing over Object between Classes not working

hey guys,
i'm new to objective-c and i'm having trouble with the memory management. i declared 3 classes, Table, Dataset and my Main class. In my mainclass, i created an Dataset Object and now im trying to pass this Object over to a Tableobject, where i want to store it permanently. but it seems to me that the garbage collector kills the reference before i can use it.
heres some code:
Dataset:
//Dataset.h
#interface Dataset : NSObject {
NSMutableArray* daten;
}
#end
//Dataset.m
#import "Dataset.h"
#import "Datensatz.h"
#implementation Dataset
- (id) init
{
self=[super init];
daten=[[NSMutableArray alloc] init];
return self;
}
Table:
//Table.h
#class Dataset;
#interface Table : NSObject {
Dataset* daten;
}
-(id)init:(NSTableView *)aTableView;
-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex;
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView;
#property (retain) Dataset* daten;
#end
//Table.m
#import "Table.h"
#import "Dataset.h"
#impl
ementation Table
#synthesize daten;
-(id)init:(NSTableView*)aTableView
{
self=[super init];
[self setDaten:[Datenmenge alloc]];
return self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
NSLog(#"anzahl: %d %#",[self.daten anzahl], self.daten);//This is always 0 null
return [daten anzahl];
}
-(void)setDaten:(Dataset *)a
{
NSLog(#"setter: anzahl: %d %#",[a anzahl], a);
[daten release];
daten=[a retain];
NSLog(#"setter: anzahl: %d %#",[daten anzahl], daten);
}
#end
In my mainclass i do the following:
//init method
[self setDaten:[[[Dataset alloc]init]autorelease]];
tabelle=[[Table alloc] init:tableview];
[tabelle setDaten:[self daten]];
Mainclass:
//code.h
//
// MalWiederWasNeuesAppDelegate.h
// MalWiederWasNeues
//
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#class Datenmenge,Graph,Tabelle;
#interface MalWiederWasNeuesAppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
NSToolbarItem *datenKnopf;
NSToolbarItem *speichernKnopf;
NSSlider *scaleSlider;
NSScroller *moveSlider;
NSTableView* tableview;
Graph* graph;
Tabelle* tabelle;
Datenmenge* daten;
}
-(void)tuWas;
- (IBAction)datenHinzufuegen:(id)sender;
- (IBAction)speichern:(id)sender;
#property (assign) IBOutlet NSWindow *window;
#property (assign) IBOutlet NSToolbarItem *datenKnopf;
#property (assign) IBOutlet NSToolbarItem *speichernKnopf;
#property (assign) IBOutlet NSSlider *scaleSlider;
#property (assign) IBOutlet NSScroller *moveSlider;
#property (assign) IBOutlet Graph *graph;
#property (assign) IBOutlet Tabelle *tabelle;
#property (assign) IBOutlet NSTableView* tableview;
#property (retain) Datenmenge* daten;
#end
//code.m
//
// MalWiederWasNeuesAppDelegate.m
// MalWiederWasNeues
//
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "MalWiederWasNeuesAppDelegate.h"
#import "Datenmenge.h"
#import "Graph.h"
#import "Tabelle.h"
#implementation MalWiederWasNeuesAppDelegate
#synthesize window;
#synthesize daten;
-(id) init
{
self.daten=[[Datenmenge alloc]init];
[self.daten datenHinzufuegen:nil];
tabelle=[[Tabelle alloc] init:tableview];
tabelle.daten=daten;
NSLog(#"konstruktor: %f %d",[daten maximum],[daten anzahl]);
//graph.daten=daten;
return self;
}
-(void)tuWas{
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (IBAction)datenHinzufuegen:(id)sender
{
NSLog(#"%f %d",[daten maximum],[daten anzahl]);
NSLog(#"daten hinzufügen");
}
- (IBAction) speichern:(id)sender
{
NSLog(#"%# %#",daten,[tabelle daten]);
NSLog(#"speichern");
}
#end
I hope this wasnt too much code for you.
when i call a method of "tabelle", my Table object, "daten" does not refer to an Dataset Object. But the NSLogs in "setDaten" show me valid references.
so, what am i doing wrong?
have a good evening,
lukas
You define Daten as a retain type
#property (retain) Dataset* daten; and #synthesize daten;
theres no need to then implement the method
-(void)setDaten:(Dataset *)a thats what #synthesize daten; does
I think theres a lost in translation moment here so ill assume Table == Tabelle and Dataset == Datmenge and I dont see the implementation for your main class.
cast your eye over this too.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html
Maybe, the daten property of your main class is declared as assign? In that case daten is correct when you call setDaten:, but might have been already autoreleased when you try to access it afterwards.
Also,
-(void)setDaten:(Dataset *)a
{
NSLog(#"setter: anzahl: %d %#",[a anzahl], a);
[daten release];
daten=[a retain];
NSLog(#"setter: anzahl: %d %#",[daten anzahl], daten);
}
is not a good implementation of a setter. If a == daten, then this object will be released (and maybe dealloc'd). You need to check identity of objects when implementing your own setter.

setting new properties in category interface/implementation

Ok, so I have this, but it wont work:
#interface UILabel (touches)
#property (nonatomic) BOOL isMethodStep;
#end
#implementation UILabel (touches)
-(BOOL)isMethodStep {
return self.isMethodStep;
}
-(void)setIsMethodStep:(BOOL)boolean {
self.isMethodStep = boolean;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(self.isMethodStep){
// set all labels to normal font:
UIFont *toSet = (self.font == [UIFont fontWithName:#"Helvetica" size:16]) ? [UIFont fontWithName:#"Helvetica-Bold" size:16] : [UIFont fontWithName:#"Helvetica" size:16];
id superView = self.superview;
for(id theView in [(UIView *)superView subviews])
if([theView isKindOfClass:[UILabel class]])
[(UILabel *)theView setFont:[UIFont fontWithName:#"Helvetica" size:16]];
self.font = toSet;
}
}
#end
If I take out the getter and setter methods then it doesn't work it tells me I need to create some getter and setter methods (or use #synthesize - but putting #synthesize in the #implementation throws an error too). But with the getter and setter methods I get an EXC_BAD_ACCESS and a crash. Any ideas? Thanks
Tom
It is not possible to add members and properties to an existing class via a category — only methods.
https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/Category.html
One possible workaround is to write "setter/getter-like" methods, that uses a singleton to save the variables, that would had been the member.
-(void)setMember:(MyObject *)someObject
{
NSMutableDictionary *dict = [MySingleton sharedRegistry];
[dict setObject:someObject forKey:self];
}
-(MyObject *)member
{
NSMutableDictionary *dict = [MySingleton sharedRegistry];
return [dict objectforKey:self];
}
or — of course — write a custom class, that inherits from UILabel
Note that nowadays an associated object can be injected during runtime. The Objective C Programming Language: Associative References
Checked all answers and did not find the most common solution:
#import <objc/runtime.h>
static void const *key;
#interface ClassName (CategoryName)
#property (nonatomic) BOOL myProperty;
#end
#implementation ClassName (CategoryName)
- (BOOL)myProperty {
return [objc_getAssociatedObject(self, key) boolValue];
}
- (void)setMyProperty:(BOOL)value {
objc_setAssociatedObject(self, key, #(value), OBJC_ASSOCIATION_RETAIN);
}
#end
swift:
private struct AssociatedKeys {
static var keyName = "keyName"
}
extension Foo {
var bar: Any! {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.keyName)
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.keyName , newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
There is actually a way, which may not be ideal, but does work.
For it to work, you will need to create a category for a class X and can only be used on subclasses of the same X (e.g. category UIView (Background) can be used with class MyView : UIView, but not directly with UIView)
// UIView+Background.h
#interface UIView (Background)
#property (strong, nonatomic) NSString *hexColor;
- (void)someMethodThatUsesHexColor;
#end
// UIView+Background.h
#implementation UIView (Background)
#dynamic hexColor; // Must be declared as dynamic
- (void)someMethodThatUsesHexColor {
NSLog(#"Color %#", self.hexColor);
}
#end
Then
// MyView.m
#import "UIView+Background.h"
#interface MyView : UIView
#property (strong, nonatomic) NSString *hexColor;
#end
#implementation MyView ()
- (void)viewDidLoad {
[super viewDidLoad];
[self setHexColor:#"#BABACA"];
[self someMethodThatUsesHexColor];
}
#end
Using this method, you will need to "redeclare" your properties, but after that, you can do all of its manipulation inside your category.
You could inject an associated object during runtime.
#import <objc/runtime.h>
#interface UIView (Private)
#property (nonatomic, assign) CGPoint initialTouchPoint;
#property (nonatomic, strong) UIWindow *alertWindow;
#end
#implementation UIView (Private)
#dynamic initialTouchPoint, alertWindow;
- (CGPoint)initialTouchPoint {
return CGPointFromString(objc_getAssociatedObject(self, #selector(initialTouchPoint)));
}
- (void)setInitialTouchPoint:(CGPoint)initialTouchPoint {
objc_setAssociatedObject(self, #selector(initialTouchPoint), NSStringFromCGPoint(initialTouchPoint), OBJC_ASSOCIATION_RETAIN);
}
- (void)setAlertWindow:(UIWindow *)alertWindow {
objc_setAssociatedObject(self, #selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIWindow *)alertWindow {
return objc_getAssociatedObject(self, #selector(alertWindow));
}
#end
EDIT: Warning: This property would have a unique value for all the instances of the class.
This worked for me, but only because I had only one instance of this class in my app.
#import <AVFoundation/AVFoundation.h>
#interface AVAudioPlayer (AstroAVAudioPlayer)
#property (nonatomic) BOOL redPilot;
#end
#import "AVAudioPlayer+AstroAVAudioPlayer.h"
#implementation AVAudioPlayer (AstroAVAudioPlayer)
BOOL _redPilot;
-(void) setRedPilot:(BOOL)redPilot
{
_redPilot = redPilot;
}
-(BOOL) redPilot
{
return _redPilot;
}
#end
A solution that I found to this was to just give each object that you want flagged a unique tag.
I made a UILabel category to add custom fonts to all my labels but on some i wanted them to be bold so i did this ->
- (void) layoutSubviews {
[super layoutSubviews];
[self addCustomFont];
}
- (void) addCustomFont {
if (self.tag == 22) {
[self setFont:[UIFont fontWithName:SEGOE_BOLD size:self.font.pointSize]];
}else{
[self setFont:[UIFont fontWithName:SEGOE_LIGHT size:self.font.pointSize]];
}
}
It seems as if since Xcode 7 (7.0.1, 7A1001), properties are supported in categories. I noticed that Xcode generates categories now for Core Data subclasses.
For example, I got the files:
Location+CoreDataProperties.h
#import "Location.h"
NS_ASSUME_NONNULL_BEGIN
#interface Location (CoreDataProperties)
#property (nullable, nonatomic, retain) NSNumber *altitude;
#property (nullable, nonatomic, retain) NSNumber *latitude;
#property (nullable, nonatomic, retain) NSNumber *longitude;
#end
NS_ASSUME_NONNULL_END
Location+CoreDataProperties.m
#import "Location+CoreDataProperties.h"
#implementation Location (CoreDataProperties)
#dynamic altitude;
#dynamic latitude;
#dynamic longitude;
#end
So looks like properties in categories might work now. I haven't tested on non-Core Data classes.
What I've noticed is that they do include the category file back into the original class:
Location.h
#interface Location : NSManagedObject
#end
#import "Location+CoreDataProperties.h"
This allows the original class to edit the properties specified by the category.